알고리즘 유형 : DP(Dynamic Programming)
풀이 참고 없이 스스로 풀었나요? : X
https://www.acmicpc.net/problem/11066
import sys
input = sys.stdin.readline
for _ in range(int(input())):
K = int(input())
files = [*map(int, input().split())]
minCost = [[0]*K for _ in range(K)] # 메모이제이션 리스트
# 연속합 (a부터 b까지의 부분연속합을 구할 때, b까지합 - (a-1)까지합 으로 구해주면 됨)
# 여러번 sum함수 안써도 되고, 딕셔너리 구해두면 O(1) 연산으로 그때그때 부분합 구해서 쓰는게 효율적
subSum = {-1: 0}
for idx in range(K):
subSum[idx] = subSum[idx-1] + files[idx]
for size in range(1, K): # size 크기로 묶은 그룹들의 minCost 구하기
for start in range(K-1): # 그룹의 시작 인덱스 범위는 0부터 K-2까지
end = start + size
# 특정 size로 그룹핑했는데 end가 벗어난다면 size 그룹핑 그만두고 다음으로 넘어가기
if end >= len(files):
break
result = float("inf")
# 어떤 구간의 최소비용 minCost는, cut을 기준으로 분할할 때, 좌측 그룹의 최소 비용 + 우측 그룹의 최소 비용 + 좌측 압축 수와 우측 압축 수 더하기
# 이 때 좌측 압축 수 + 우측 압축 수는 그 구간의 모든 수의 합과 같음
for cut in range(start, end):
result = min(result, minCost[start][cut] + minCost[cut+1][end] + subSum[end] - subSum[start-1])
minCost[start][end] = result
print(minCost[0][-1])
풀이 요약
해당 풀이는 O(N)이다. C++ 등에서는 AC가 뜨는데, python3에서는 TLE가 뜨고 pypy3으로 제출하면 AC가 뜬다.
python3으로 통과하기 위해선 크누스 최적화 기법을 적용해야한다. 관심이 있다면 따로 알아보고 적용해서 풀어보도록 하자. 필자는 너무 어렵기도 하고 시간을 너무 잡아먹어서, 학습에 비효율적이라고 판단해서 그냥 DP 풀이로 만족했다...
문제의 조건 중 "연속적으로 파일을 합쳐라"라는 부분을 주의해야한다.
이 조건에 주의하면서 DP 점화식을 세워보자. 부분 문제를 어떻게 정의해야할까?
우선 생각해보자. 어떤 방식으로 합쳐나가든, 마지막 이전 단계에서는 합쳐진 숫자 두 개만 남고, 그 두 개를 합치면 완료되게 된다. 따라서,
이런 아이디어를 떠올릴 수 있다.
점화식스럽게 다시 표현하자면
"전체 그룹의 압축 최소비용 = 두 그룹으로 분할하고, 각 그룹의 압축 최소비용의 합 + 압축 완료된 두 숫자의 합"
딱 봐도 재귀의 구조가 보인다. 재귀 구조에 메모이제이션을 활용한 DP로 풀 수 있겠다는 생각이 든다.
이제 본격적으로 구현을 해보자.
K = 소설 파일 수
files = 입력받는 파일 크기들 리스트
minCost = 2차원 리스트이다. minCost[a][b]는 files의 인덱스 a부터 b까지(b 포함)에 해당하는 하나의 그룹의 압축 최소비용을 의미한다.
subSum = 딕셔너리이다. files의 어떤 idx까지의 연속합
top-down 방식이 아닌 bottom-up 방식으로 풀 것이다. 재귀 방식으로 푸니 pypy3으로도 TLE가 난다.
전체적인 흐름대로 설명하겠다.
가장 바깥 for문이다.
두번째 for문이다.
minCost[start][end] = min(minCost[start][cut] + minCost[cut+1][end] + subSum[end] - subSum[start-1] for cut in range(start, end))
for cut in range(start, end)
가 의미하는 바이다.배운 점, 어려웠던 점