알고리즘 공부/완전탐색

백준 1806 (부분합)

kdhoooon 2021. 4. 25. 15:32

문제


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

 

 

 

풀이


연속된 수들의 부분합이기 때문에 두개의 포인터를 이용해서 구했다.

 

5 1 3 5 10 7 4 9 2 8

이렇게 배열이 있다면, left = 0 right = 0 부터 시작해서 S보다 크거나 같으면 left 값을 빼주고 s 보다 작다면 right값을 더해주는 방식으로 풀었다.

while(true) {
			
	if(sum >= s) {
		sum -= arr[left++];
	}
	else if(right >= arr.length) {
		break;
	}
	else if(sum < s) {
		sum += arr[right++];
	}
	
	if(sum >= s){
		min = Math.min(min, right - left);
	}
}

위 코드를 보면 이해하기 쉬울 것이다.

S 이상이 됐을 때, 최소갯수를 구하는 것이 문제이므로 그때 최솟값을 저장해준다.

 

시간복잡도를 줄이는 것이 어려워 보이지만 두 포인터를 이용한 완전탐색을 알고있었다면 반복문을 여러개 사용하지 않고 풀 수있다.

 

 

<전체코드>

import java.io.*;
import java.util.*;

public class Main {	

	static StringBuilder sb = new StringBuilder();
	
	static int n, s;
	static long[] arr;

	public static void main(String[] args) throws IOException{
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
		
		StringTokenizer st = new StringTokenizer(bufferedReader.readLine());
		n = Integer.parseInt(st.nextToken());
		s = Integer.parseInt(st.nextToken());
		
		st = new StringTokenizer(bufferedReader.readLine());
		arr = new long[n];
		for(int i = 0 ; i < n ; i++) {
			arr[i] = Long.parseLong(st.nextToken());
		}
		
		int left = 0, right = 0;
		long sum = 0;
		int min = Integer.MAX_VALUE;
		while(true) {
			
			if(sum >= s) {
				sum -= arr[left++];
			}
			else if(right >= arr.length) {
				break;
			}
			else if(sum < s) {
				sum += arr[right++];
			}
			
			if(sum >= s){
				min = Math.min(min, right - left);
			}
		}
		
		if(min == Integer.MAX_VALUE) {
			min = 0;
		}
		
		System.out.println(min);
	}	
}