[백준] #2003 수들의 합2(python)

수영·2022년 8월 28일

백준

목록 보기
50/117
post-thumbnail

📌문제

N개의 수로 된 수열 A[1], A[2], …, A[N] 이 있다. 이 수열의 i번째 수부터 j번째 수까지의 합 A[i] + A[i+1] + … + A[j-1] + A[j]가 M이 되는 경우의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N(1 ≤ N ≤ 10,000), M(1 ≤ M ≤ 300,000,000)이 주어진다. 다음 줄에는 A[1], A[2], …, A[N]이 공백으로 분리되어 주어진다. 각각의 A[x]는 30,000을 넘지 않는 자연수이다.

출력

첫째 줄에 경우의 수를 출력한다.

예제 입력1

4 2
1 1 1 1

예제 출력1

3

예제 입력2

10 5
1 2 3 4 2 5 3 1 1 2

예제 출력2

3

백준 2003번 문제

💡Idea

투 포인터로 해결할 수 있는 문제입니다.

수열의 숫자들을 가리키는 포인터 변수 두 개를 가지고, 두 변수를 이동시키면서 변수 사이의 숫자들의 합을 확인하는 식으로 문제를 해결하였습니다.

  1. 숫자들의 합이 M인 경우
    정답의 개수를 하나 증가한 뒤, 왼쪽의 포인터를 한 칸 이동해줍니다.
  2. 숫자들의 합이 M보다 큰 경우
    합이 더 커 포함되는 숫자의 수를 줄여야하므로, 왼쪽의 포인터를 한 칸 이동해줍니다.
  3. 숫자들의 합이 M보다 작은 경우
    합이 더 작아 더 많은 숫자를 포함시켜야 하므로, 오른쪽의 포인터를 한 칸 이동해줍니다.

💻코드1

  • ⏰ 시간 : 588 ms / 메모리 : 30840 KB
import sys
input = sys.stdin.readline
N, M = map(int, input().split())
A = list(map(int, input().split()))

left, right = 0, 1 # 왼쪽과 오른쪽을 가리키는 포인터
ans = 0
while left <= right <= N:
    total = sum(A[left:right]) # left 포함 right 미포함 사이 합 계산
    if total == M: # 합이 M인 경우 왼쪽 포인터 한 칸 이동
        ans += 1
        left += 1
    elif total < M: # 합이 M보다 작은 경우 오른쪽 포인터 한 칸 이동
        right += 1
    else: # 합이 M보다 큰 경우 왼쪽 포인터 한 칸 이동
        left += 1

print(ans)

📝코드 설명1

leftright의 두 포인터를 이동하면서, 두 포인터 사이의 합들을 계속해서 계산해서 계산한 합이 M이 되는지를 확인하는 코드입니다.

while 문 내에서 A 배열의 slicing과 sum 계산이 반복되기 때문에 시간이 오래 걸립니다.

따라서 매번 구간합을 새로 계산하지 않고, leftright의 이동에 따라 구간에 새로 더해지거나 빠지는 숫자들만을 더하고 빼준다면, O(N)O(N)의 시간복잡도로 빠르게 문제를 해결할 수 있습니다.

💻코드2

  • ⏰ 시간 : 72 ms / 메모리 : 30840 KB
import sys
input = sys.stdin.readline
N, M = map(int, input().split())
A = list(map(int, input().split())) + [0]

ans = left = 0
right = 1
sum_nums = A[0] # 시작 구간 합 : sum(A[0:1])
while left <= right <= N:
    if sum_nums > M: # 합이 M보다 큰 경우, left 값을 빼주고 left 한 칸 이동
        sum_nums -= A[left]
        left += 1
    elif sum_nums < M: # 합이 M보다 작은 경우, right 값을 더해주고 right 한 칸 이동
        sum_nums += A[right]
        right += 1
    else: # 합이 M인 경우, left 값 빼주고 left 한 칸 이동
        sum_nums -= A[left]
        left += 1
        ans += 1
print(ans)

📝코드 설명2

매번 구간합을 새로 계산하지 않고, 구간의 변화에 따라 숫자들만 더하고 빼주면서 합을 구해가는 방식의 코드입니다.

코드1에 비하여 시간이 대폭 줄어든 것을 확인할 수 있습니다.

A 리스트의 마지막에 [0]을 더해준 이유는, 반복문 내에서 rightN보다 커지는 경우를 확인해야하는 경우가 있는데, 그 조건을 매번 확인하지 않고 index overflow가 나지 않도록 더미값을 더해준 것입니다.

백준 미로 탐색 문제에서 리스트를 0으로 감싸준 것과 같은 맥락입니다.

profile
하고 싶은 건 그냥 죽도록 합니다

0개의 댓글