알고리즘/문제 풀이

1806번: 부분합

Themion 2021. 12. 10. 09:57

https://www.acmicpc.net/problem/1806

 

1806번: 부분합

첫째 줄에 N (10 ≤ N < 100,000)과 S (0 < S ≤ 100,000,000)가 주어진다. 둘째 줄에는 수열이 주어진다. 수열의 각 원소는 공백으로 구분되어져 있으며, 10,000이하의 자연수이다.

www.acmicpc.net

배열의 특정 값을 가리키는 두 포인터 l, r이 있다고 하자.

l부터 r까지의 부분합 s에 대해, s가 S 미만이라면 r을 1 늘리고, 그렇지 않다면 l을 1 늘리며 연속합을 갱신한다면 빠른 속도로 연속합의 최소 길이를 구할 수 있다.

#include <iostream>

using namespace std;

#define MAX_N 100000

int min(int a, int b) { return a < b ? a : b; }

int main() {
    // 입출력 속도 향상
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    // N: 배열의 길이, S: 목표 연속합의 최소 크기
    // l, r: 투 포인터에 쓸 인덱스, sum: 투 포인터로 구한 연속합
    // ans: 크기가 S 이상인 연속합의 최소 길이
    int N, S, arr[MAX_N], l = 0, r = 1, sum, ans = MAX_N + 1;

    // 문제의 조건을 입력받은 뒤
    cin >> N >> S;
    for (int i = 0; i < N; i++) cin >> arr[i];
    // 연속합을 배열의 첫 성분으로 초기화
    sum = arr[0];

    // 투 포인터 실행
    while (l < N) {
        // 연속합이 S 미만이면서 더할 성분이 있다면 연속합 오른쪽의 성분을 더한다
        if (sum < S && r < N) sum += arr[r++];
        // 그렇지 않다면
        else {
            // 연속합이 S 이상일 때 연속합의 최소 길이를 갱신
            if (sum >= S) ans = min(ans, r - l);
            // 연속합의 맨 왼쪽 수를 제거
            sum -= arr[l++];
        }
    }
    
    // 조건을 만족하는 부분합을 찾았다면 그 길이를, 아니라면 0을 출력
    cout << (ans < MAX_N + 1 ? ans : 0) << '\n';

    return 0;
}

'알고리즘 > 문제 풀이' 카테고리의 다른 글

1865번: 웜홀  (0) 2021.12.10
1850번: 최대공약수  (0) 2021.12.10
1786번: 찾기  (0) 2021.12.09
1780번: 종이의 개수  (0) 2021.12.09
1764번: 듣보잡  (0) 2021.12.09