https://www.acmicpc.net/problem/11066
문제
소설가인 김대전은 소설을 여러 장(chapter)으로 나누어 쓰는데, 각 장은 각각 다른 파일에 저장하곤 한다. 소설의 모든 장을 쓰고 나서는 각 장이 쓰여진 파일을 합쳐서 최종적으로 소설의 완성본이 들어있는 한 개의 파일을 만든다. 이 과정에서 두 개의 파일을 합쳐서 하나의 임시파일을 만들고, 이 임시파일이나 원래의 파일을 계속 두 개씩 합쳐서 소설의 여러 장들이 연속이 되도록 파일을 합쳐나가고, 최종적으로는 하나의 파일로 합친다. 두 개의 파일을 합칠 때 필요한 비용(시간 등)이 두 파일 크기의 합이라고 가정할 때, 최종적인 한 개의 파일을 완성하는데 필요한 비용의 총 합을 계산하시오.
예를 들어, C1, C2, C3, C4가 연속적인 네 개의 장을 수록하고 있는 파일이고, 파일 크기가 각각 40, 30, 30, 50 이라고 하자. 이 파일들을 합치는 과정에서, 먼저 C2와 C3를 합쳐서 임시파일 X1을 만든다. 이때 비용 60이 필요하다. 그 다음으로 C1과 X1을 합쳐 임시파일 X2를 만들면 비용 100이 필요하다. 최종적으로 X2와 C4를 합쳐 최종파일을 만들면 비용 150이 필요하다. 따라서, 최종의 한 파일을 만드는데 필요한 비용의 합은 60+100+150=310 이다. 다른 방법으로 파일을 합치면 비용을 줄일 수 있다. 먼저 C1과 C2를 합쳐 임시파일 Y1을 만들고, C3와 C4를 합쳐 임시파일 Y2를 만들고, 최종적으로 Y1과 Y2를 합쳐 최종파일을 만들 수 있다. 이때 필요한 총 비용은 70+80+150=300 이다.
소설의 각 장들이 수록되어 있는 파일의 크기가 주어졌을 때, 이 파일들을 하나의 파일로 합칠 때 필요한 최소비용을 계산하는 프로그램을 작성하시오.
입력
프로그램은 표준 입력에서 입력 데이터를 받는다. 프로그램의 입력은 T개의 테스트 데이터로 이루어져 있는데, T는 입력의 맨 첫 줄에 주어진다.각 테스트 데이터는 두 개의 행으로 주어지는데, 첫 행에는 소설을 구성하는 장의 수를 나타내는 양의 정수 K (3 ≤ K ≤ 500)가 주어진다. 두 번째 행에는 1장부터 K장까지 수록한 파일의 크기를 나타내는 양의 정수 K개가 주어진다. 파일의 크기는 10,000을 초과하지 않는다.
출력
프로그램은 표준 출력에 출력한다. 각 테스트 데이터마다 정확히 한 행에 출력하는데, 모든 장을 합치는데 필요한 최소비용을 출력한다.
해설
다이나믹 프로그래밍의 응용된 부분으로 어떤 값을 정하는 게 중요한 문제였다.
먼저 start와 end가 파일 용량의 인덱스 값이라고 할 때 dp[start][end]는 start부터 end까지 압축할 때 최소 값을 의미한다.
예를 들어 문제에서 주어진 파일 용량 값이 [1,2,3,4,5,6]이고 dp[1][4]을 예로 들어보자 dp[1][4]는 1부터 4까지 압축하는 경우이므로
[2, 3, 4, 5]가 압축될 때의 최소 값이다. 즉 [2,3,4,5]를 순서를 다르게 해서 압축했을 때 최소값이 dp[1][4]의 값이 될 것이다.
[2, 3, 4, 5]를 압축하는 경우는 아래와 같다.
[{2},{3,4,5}] [{2,3},{4,5}] [{2,3,4},{5}]
즉 이를 다시 dp로 표현해 본다면
[{2},{3,4,5}] = dp[1][1] + dp[2][4]
[{2,3},{4,5}] = dp[1][2] + dp[3][4]
[{2,3,4},{5}] = dp[1][3] + dp[4][4]
즉 dp[1][4]의 값은 위의 3개의 값중 가장 작은 최소값이고 이 값에 추가로 첫번째부터 네번째 값까지 합을 더해주어야 한다.
이 값을 더해주는 이유는 파일 집합을 압축할 때 원소들의 합만큼 추가로 비용이 소요되기 때문이다.
이를 구현 할 때 주의 해야되는 점은 dp[start][end] 와 dp[end][start]는 사실 상 동일하기 때문에 start < end라는 조건을 구현해야한다. 이를 위해 시작값을 정해두고 gap을 바꿔가면서 값을 업데이트 하면된다.
import sys
input = sys.stdin.readline
#테스트 케이스의 개수
T = int(input())
INF = sys.maxsize
for _ in range(T):
#파일 개수
K = int(input())
#file_sizes[i]: i번쨰 파일의 크기
file_sizes = list(map(int, input().split()))
#dp[start][end]: start에서 end까지의 파일을 압축할 떄 드는 최소 비용
dp = [[0 for i in range(K)] for j in range(K)]
#S[i] : 첫번째 원소부터 i번째 원소까지의 합
S = [sum(file_sizes[0:i+1]) for i in range(K)]
#gap를 고정시키고 start를 늘려가면서 값을 업데이트
for gap in range(1, K):
#시작값은 최대 : 전체 개수 - gap -1 이런식으로 설정하지 않으면 IndexError 발생
for start in range(K - gap):'
#끝 값 지정
end = start + gap
#먼저 값을 가장 큰 값으로 고정해두고
dp[start][end] = INF
#고정된 집합안에서 부분집합을 나눠가면서 최소 값을 찾는다.
for k in range(start, end):
dp[start][end] = min(dp[start][end], dp[start][k] + dp[k+1][end])
#업데이트가 완료되면 이번 단계에서 압축 비용인 현재 집합의 원소의 합을 더해준다.
dp[start][end] += S[end] - S[start-1] if start != 0 else S[end]
print(dp[0][-1])
'Algorithm > 백준 온라인 저지(BOJ)' 카테고리의 다른 글
[BOJ] 백준 16437 양 구출 작전 python (0) | 2021.07.26 |
---|---|
[BOJ] 백준 1693 트리 색칠하기 python (0) | 2021.07.25 |
[BOJ] 1520 내리막길 python (0) | 2021.05.23 |
[BOJ] 1707 이분그래프 python (0) | 2021.05.19 |
[BOJ] 1504 특정한 최단경로 python (0) | 2021.05.19 |