JAVA 알고리즘/유형 문제

[백준 1806 java 자바]부분합- 투포인터(부분합 효율성)

nomoreFt 2022. 10. 17. 22:45

[Gold IV] 부분합 - 1806

문제 링크

성능 요약

메모리: 25712 KB, 시간: 308 ms

분류

누적 합(prefix_sum), 두 포인터(two_pointer)

문제 설명

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

입력

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

출력

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



풀이


투 포인터로 풀면 된다. 배열에서 효율적으로 부분적인 합 또는 경우를 구할 경우 dfs같은 전체 반복이 아니라 오른쪽으로 진행하면서 경우의 수를 따지면 된다.

왼쪽과 오른쪽 두 개의 포인터를 두고 오른쪽 포인터를 -> 진행하면서 경우에 벗어나는 경우 왼쪽 포인터를 -> 옮기면서 진행한다.

투포인터 부분합의 핵심적인 부분이다.

 int left = 0, right = 0, sum = 0, min = Integer.MAX_VALUE;
 //왼쪽, 오른쪽 포인터와 왼쪽과 오른쪽 포인터 사이의 부분합이 조건을 만족하는지 구분할 총 합, 길이의 최소값을 담을 min

 //매 시행마다 1.왼오 사이 부분합이 조건에 벗어나는가? 그럼 왼쪽을 당긴다.
              2.만약 왼쪽을 다 당겨봤는데도 오른쪽 포인터가 끝에 닿았으면 조건을 만족할 부분합의 경우의 수가 없으므로 종료.
             3. 기본적으로 오른쪽을 최대한 늘리면서 조건을 최대한 만족하는 곳 까지 땡긴다.
             4. 매 시행의 마지막에는 부분합이 조건과 일치하는지 체크
        while (true) {
            if (sum >= S) {
                sum -= arr[left++];
            } else if (right == N) {
                break;
            }else{
                sum += arr[right++];
            }
            if (sum >= S) {
                min = Math.min(min, right - left);(최소 길이를 구해야 하기 때문에 오른쪽과 왼쪽 차)
            }
        }

코드


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;

import static java.util.Arrays.stream;

class Main {
    static int N, S;
    static int[] arr;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] input = br.readLine().split(" ");
        N = Integer.parseInt(input[0]);
        S = Integer.parseInt(input[1]);
        arr = stream(br.readLine().split(" ")).mapToInt(Integer::parseInt).toArray();

        int left = 0, right = 0, sum = 0, min = Integer.MAX_VALUE;

        while (true) {
            if (sum >= S) {
                sum -= arr[left++];
            } else if (right == N) {
                break;
            }else{
                sum += arr[right++];
            }
            if (sum >= S) {
                min = Math.min(min, right - left);
            }
        }
        System.out.println(min == Integer.MAX_VALUE ? 0 : min);
    }
}