Algorithm (PS)

[백준] 10986번: 나머지 합 (Python) - 메모리 초과와 누적합

minjiwoo 2023. 9. 29. 10:07
728x90

https://www.acmicpc.net/problem/10986

 

10986번: 나머지 합

수 N개 A1, A2, ..., AN이 주어진다. 이때, 연속된 부분 구간의 합이 M으로 나누어 떨어지는 구간의 개수를 구하는 프로그램을 작성하시오. 즉, Ai + ... + Aj (i ≤ j) 의 합이 M으로 나누어 떨어지는 (i, j)

www.acmicpc.net

1. 첫번째 시도 -> 메모리 초과 발생

256MB 

  • int형 == 4Byte
  • 1KB == 1024Byte
  • 1MB == 1024KB
  • 256MB = 256 * 1024KB = 256 * 1024 * 1024B = int형 256 * 1024 * 1024 / 4Byte = 67108864개

 

[아이디어]

  • 2차원 배열 dp 를 생성하고 여기에 i부터 j 까지의 누적합을 저장한다. 
  • 저장된 누적합 2차원 배열을 하나씩 순회하면서 M으로 나눈 나머지를 확인한다. 
# https://www.acmicpc.net/problem/10986
N, M = map(int, input().split())
data = list(map(int, input().split()))
answer = 0
dp = [[0] * (N+1) for _ in range(N+1)] # 누적합 저장 dp[i][j] 는 i ~ j 까지의 합

# 누적합 저장하기
for i in range(N):
    for j in range(i, N):
        if i == j:
            dp[i][j] = data[i]
        else:
            dp[i][j] = dp[i][j-1] + data[j]

# check dp
# for i in range(N):
#     print(*dp[i])

# 누적합을 M 으로 나누기
for i in range(N):
    for j in range(N):

        if dp[i][j] != 0 and dp[i][j] % 3 == 0:
            answer += 1
print(answer)

 

2. 두번째 시도 -> 시간 초과

[아이디어]

  • dp 를 1차원배열으로 바꾸고, 누적합을 사용하여 i ~ j 구간의 합을 구하자 !
  • 메모리 초과는 방지했으나 시간초과가 발생했음. 현재 구간 누적합을 O(n**2) 시간복잡도로 구하고 있어서 시간 초과가 발생한다. 
# https://www.acmicpc.net/problem/10986
import sys

input = sys.stdin.readline
N, M = map(int, input().split())
data = list(map(int, input().split()))
answer = 0
dp = [0] * N
dp[0] = data[0]
for i in range(1, N):
    dp[i] = dp[i-1] + data[i]

# 누적합을 M 으로 나누기,
for i in range(N-1):
    for j in range(i, N):
        prefix_sum = dp[j] - dp[i-1]
        if prefix_sum % 3 == 0:
            answer += 1

 

[최종 풀이]

풀이를 참고해보니 몫들을 누적합으로 저장해서 직접 나누는 것이 아니라 '나머지'들만 따로 저장하여 나머지가 0 인 구간을 구하는 방식으로 푸는 아주아주 참신한 풀이가 있었다 ... 

# https://www.acmicpc.net/problem/10986
import sys

input = sys.stdin.readline
N, M = map(int, input().split())
data = list(map(int, input().split()))
answer = 0
dp = [0] * M # 나머지를 저장하는 배열 , size가 M 이 된다.
prefix_sum = 0
for i in range(N):
    prefix_sum += data[i]
    dp[prefix_sum%M] += 1 # 나머지가 몇개인지 저장하기

# 누적합을 M 으로 나누기,
answer = dp[0] # 나머지가 0 인 경우는 무조건 포함시키기 

# iC2 조합으로 구하기 , 나머지가 같은 구간 2개를 뽑는 모든 경우의 수 
for i in dp:
    answer += i*(i-1)//2 
print(answer)

 

참고 자료 

https://book.acmicpc.net/algorithm/prefix-sum

 

누적 합

배열의 시작 인덱스위의 설명에서 배열 $A$의 시작 인덱스는 $1$로 사용했습니다. 그 이유는 $S[l-1]$ 때문입니다. 시작 인덱스가 $1$이면 $l$의 최솟값은 $1$이고, 여기서 $l-1$은 $0$입니다. 만약, 시작

book.acmicpc.net

 

728x90