Home [1806] 부분합
Post
Cancel

[1806] 부분합

문제 링크 : https://www.acmicpc.net/problem/1806

1806 - 부분합

본문

$10\,000$ 이하의 자연수로 이루어진 길이 $N$짜리 수열이 주어진다. 이 수열에서 연속된 수들의 부분합 중에 그 합이 $S$ 이상이 되는 것 중, 가장 짧은 것의 길이를 구하는 프로그램을 작성하시오.

입력

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

출력

첫째 줄에 구하고자 하는 최소의 길이를 출력한다. 만일 그러한 합을 만드는 것이 불가능하다면 0을 출력하면 된다.

제한

시간 제한메모리 제한
0.5sec128MB

풀이

문제의 제목은 부분합인데 실제로는 연속합을 구하는 문제이다. 투 포인터를 활용하면 쉽게 해결할 수 있는 문제이다.

시작점에 양 끝 포인터를 가져다 두고, 그 사이의 합을 기록해둔다. 그 값을 $d$라고 할 때, 아래와 같은 절차만으로도 문제를 해결할 수 있다.

  • $d < S$인 경우 : 오른쪽 포인터를 1칸 뒤로 이동시킨다
  • $d \geq S$인 경우 : 구간의 길이를 정답의 후보로 기록한다. 왼쪽 포인터를 1칸 뒤로 이동시킨다

사실 설명은 이것만으로도 끝난다. 문제에서 원하는 바를 if-else 하나로 해결할 수 있는 문제.

코드

사용 언어 : C

최종 수정일 : 2024 / 3 / 5

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
#include <stdio.h>

#define MAX_IDX 100000

int arr[MAX_IDX];
int n;

int sum, s;
int l, r;
int result = MAX_IDX + 1;

#define min(a, b) (((a) > (b)) ? (b) : (a))

int main() {
    scanf("%d %d", &n, &s);
    for (int i = 0; i < n; ++i) {
        scanf("%d", arr + i);
    }

    while (r < (n + 1)) {
        if (sum >= s) {
            result = min(result, r - l);
            sum -= arr[l++];
        } else {
            sum += arr[r++];
        }
    }

    if (result > MAX_IDX) {
        printf("0");
    } else {
        printf("%d", result);
    }
    return 0;
}
This post is licensed under CC BY 4.0 by the author.