투포인터 알고리즘이란?
1차원 배열에서 각자 다른 원소를 가리키고 있는
2개의 포인터를 이용해 문제를 해결하는 알고리즘이다.
대표적인 문제는 백준 1806 구간합 문제이다.
https://www.acmicpc.net/problem/1806
문제를 해설하며 알고리즘도 같이 설명하겠다.
문제해설
길이 n인 수열이 주어진다.
목표값이 주어지고
수열의 부분합 중에서 목표값 이상인
최소의 부분합 길이를 출력하면 된다.
문제풀이
투포인터 알고리즘의 핵심은 아래 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
while (right >= left) {
int nr = right, nl = left;
if (sum < t) {
if (right < n) {
right++;
sum += arr[right];
}
}
else {
if (left <= right) {
sum -= arr[left];
left++;
}
}
if (sum >= t)
res = min(res, right - left + 1);
if (nr == right && nl == left)
break;
}
코드만 봐서는 이해가 안되니 바로 아래 그림으로 설명하면
입력 수열은 5 1 3 5 10 7 4 9 2 8
이고 목표값은 15이다.
우선 right
, left
둘다 초기값인 0을 가지고있다.
right
가 한칸 이동하며 합도 0부터 1까지인 5로 바뀌었다.
목표값이 도달하지 못했기 때문에 right
가 한칸 더 움직인다.
마찬가지로 한칸 더 움직인다.
1이 부족하다 한칸더
이제 0부터 5까지의 합이 24가 되었다.
목표치인 15를 넘었지만 우리는 최소길이의 수열을 찾아야 한다.
그러므로 이제 left
가 한칸씩 움직인다.
left
가 2가 되면서 1의 값인 5가 합에서 빠졌다.
left
가 한칸 더 움직여서 -1이 되었다.
이제 left
가 4까지 움직여서 4~5까지의 합이 15가 되었다.
다음 단계는 left
가 한칸 더 움직여 합이 10이 되고
15보다 합이 작기 때문에 right
가 움직이면서 다시 값이 커질 것이다.
이런식으로 배열 끝까지 탐색을 해주며 값을 갱신한다.
이 문제를 이중반복문으로 푼다면 시간복잡도가 O(N^2)이 되겠지만
투포인터 알고리즘을 쓴다면 탐색시간이 2N이므로 O(N)만에 문제가 풀린다.
전체코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<iostream>
#include<algorithm>
#include<vector>
#include<limits.h>
using namespace std;
typedef long long ll;
int main() {
ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
int n, t; cin >> n >> t;
vector<int>arr(n + 1);
for (int i = 1; i <= n; i++)
cin >> arr[i];
ll sum = 0;
int right = 0, left = 0, res = INT_MAX;
while (right >= left) {
int nr = right, nl = left;
if (sum < t) {
if (right < n) {
right++;
sum += arr[right];
}
}
else {
if (left <= right) {
sum -= arr[left];
left++;
}
}
if (sum >= t)
res = min(res, right - left + 1);
if (nr == right && nl == left)
break;
}
if (res == INT_MAX)
cout << 0;
else
cout << res;
return 0;
}