연속된 부분 수열의 합
연속된 부분 수열의 합
문제 링크
아이디어
- 가장 단순하게 생각하여 모든 부분 수열을 구하는 방법은 \(O(N^2)\) 의 시간 복잡도를 요구한다.
- 배열의 크기가 \(10^6\) 이므로, 이는 시간 제한에 걸린다.
- 비내림차 수열이 주어진다면, \(\sum_{k=i}^{j} S_k < \sum_{k=i}^{j+1} S_k\) 를 만족한다. (\(S_k \ge 1\))
- 또한, \(\sum_{k=i+1}^{j} S_k < \sum_{k=i}^{j} S_k\) 도 만족한다.
- 부분 수열은 연속된 인덱스로 구성된다.
- 즉, \([i, j]\) 에 대해서 \(\sum_{i}^{j} S\) 를 구하는 것
- 문제에서 요구하는 것은 \(\sum_{i}^{j} S = k\) 가 되는 \([i, j]\)의 집합의 원소
- 2, 3번에서 기인하여 아래와 같은 결론을 도출
- \(\sum_{l=i}^{j} S_l < k\) 라면, \(j\) 을 증가시켜서 부분 수열의 합을 크게 만든다.
- \(\sum_{l=i}^{j} S_l > k\) 라면, \(i\) 를 증가시켜서 부분 수열의 합을 적게 만든다.
4번에서 기술한 것처럼 구현한다면, 현재까지의 누적합 정보를 유지하면서 새로운 원소들에 대해서만 탐색을 진행한다.
또한, 탐색 범위는 항상 후보해를 포함하므로 항상 최적해를 구할 수 있음이 보장된다. (엄밀한 증명은 노트로..)
알고리즘의 시간 복잡도는 \(i, j\) 가 최대 증가할 수 있는 범위가 sequence의 크기이므로 \(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
#include <string>
#include <vector>
using namespace std;
vector<int> solution(vector<int> sequence, int k) {
vector<int> answer = {-1, 0x3f3f3f3f};
// [i, j)
int i = 0, j = 0, ac_sum = 0;
const int size = sequence.size();
while (j <= size) {
if (i < j && ac_sum >= k) {
if (ac_sum == k && answer[1] - answer[0] > j - i) {
answer[0] = i, answer[1] = j;
}
ac_sum -= sequence[i++];
} else {
ac_sum += sequence[j++];
}
}
answer[1]--;
return answer;
}
This post is licensed under CC BY 4.0 by the author.