[백준] 1806번 부분합 | 파이썬 Python

문제

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

 

입력

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

 

출력

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

 

예제

 

나의 풀이1

N, S = map(int, input().split())
nums = sorted(list(map(int, input().split())))

start, end = 0, 1
mn = N + 1

while start < end and end < N:
    partial_sum = sum(nums[start:end+1])
    length = end - start + 1

    if partial_sum == S:
        if length < mn:
            mn = length
        start += 1
        end += 1
    elif partial_sum > S:
        if length < mn:
            mn = length
        start += 1
    elif partial_sum < S:
        end += 1

result = mn if mn <= N else 0
print(result)
  • 부분합이 타겟보다 큰지, 작은지, 같은지에 따라 두 포인터를 이동하면서 최소 길이의 수열을 찾는 방법으로 풀어봤다.
  • 이 풀이는 시간 초과로 실패했다.

 

나의 풀이2

N, S = map(int, input().split())
nums = sorted(list(map(int, input().split())))

mn = N + 1

for start in range(N):
    end = start + 1

    while sum(nums[start:end+1]) < S and end < N:
        end += 1
    
    length = end - start + 1
    if sum(nums[start:end+1]) >= S and length < mn:
        mn = length
   
result = mn if mn <= N else 0
print(result)
  • for문을 실행하면서 `1`부터 `N-1`까지를 `start`로 가지는 수열의 부분합이 `S`이상인 최소 길이를 구한다.
  • 이 풀이도 시간 초과로 실패했다.

 

다른 사람의 풀이

N, S = map(int, input().split())
nums = list(map(int, input().split()))

left, right = 0, 0
sum = 0
min_length = N + 1

while True:
    if sum >= S:
        min_length = min(min_length, right-left)
        sum -= nums[left]
        left += 1
    elif right == N:
        break
    else:
        sum += nums[right]
        right += 1

result = 0 if min_length == N + 1 else min_length
print(result)
  • 두 포인터 모두 `0`부터 시작해서 현재 수열의 부분합이 타겟보다 크다면 `left`를 한 칸 이동하고, 부분합이 작다면 `right`을 한 칸 이동한다.
  • 만약 오른쪽 포인터가 `N`에 도달한다면 while문을 종료한다.
    • 부분합이 타켓보다 큰 경우의 if문 다음에 elif문에서 while문 종료를 설정한 이유는 오른쪽 포인터가 리스트의 끝에 도달했더라도 왼쪽 포인터를 이동하면서 새로운 수열의 부분합을 구할 수 있어야 하기 때문이다.

 

회고

  • 비슷한 듯 다른 투포인터 알고리즘 문제들
  • 기본적인 접근 방식은 맞히더라도 디테일의 차이로 문제를 계속 틀리고 있어서 아쉽다.
  • 좀 더 세심하게, 시간복잡도를 고려하면서 문제를 풀어 보자!