[백준] #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

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

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

따라서 매번 구간합을 새로 계산하지 않고, left와 right의 이동에 따라 구간에 새로 더해지거나 빠지는 숫자들만을 더하고 빼준다면, 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]을 더해준 이유는, 반복문 내에서 right가 N보다 커지는 경우를 확인해야하는 경우가 있는데, 그 조건을 매번 확인하지 않고 index overflow가 나지 않도록 더미값을 더해준 것입니다.

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

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

0개의 댓글