알고리즘/백준

11066 파일 합치기

흰싸라기 2022. 4. 1. 00:43

백준 11066번 파일 합치기 : https://www.acmicpc.net/problem/11066

 

11066번: 파일 합치기

소설가인 김대전은 소설을 여러 장(chapter)으로 나누어 쓰는데, 각 장은 각각 다른 파일에 저장하곤 한다. 소설의 모든 장을 쓰고 나서는 각 장이 쓰여진 파일을 합쳐서 최종적으로 소설의 완성본

www.acmicpc.net

 

[풀이 전]

예를 들어, 40 30 30 50의 입력을 받으면

1 : 40 / 30 /  30 /  50

2 : 40 30 / 30 30 / 30 50

3 : 40 30 30 / 30 30 50

4 : 40 30 30 50

와 같은 순서로 1부터 4까지 파일 묶음의 최소 비용을 순서대로 구해가면 될 것이라 생각했다. ( ex. 3 : 40 30 30의 최소비용 구하기)  

 

최소 비용은 어떤 식으로 구할 것인가? 만약 파일 4개를 묶는다면 '1+3', '2+2', '3+1'의 경우가 있다.

즉, 저 세개의 경우 중 최소비용을 저장하면 된다. 

 

① 1부터 N까지 합쳐지는 파일의 개수를 확장하며 각 경우의 최소바용을 구해간다.

② 파일의 개수가 i일 때, 

i = 1+(i-1), 2+(i-2), 3+(i-3)... 으로 나누어 각각의 값을 계산해 최소비용을 구해 저장한다.  

 

 

[문제 풀이]

#include <iostream>
#include <climits>
using namespace std;
#define NMAX 501

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int T;
	cin >> T;
	for (int i = 0; i < T; i++) {
		int dp[NMAX][NMAX] = {};
		int N, K;
		cin >> N;
		for (int i = 1; i <= N; i++) {
			cin >> K;
			dp[1][i] = K;
		}

		for (int i = 2; i <= N; i++) { // 파일의 개수
			for (int k = 1; k <= N - i + 1; k++) { 
				int kmin = 0;
				for (int j = 1; j <= i - 1; j++) { // 이전까지 파일을 합치는데 들었던 비용
					int temp = 0;
					if (j > 1)temp += dp[j][k];
					if (i - j > 1) temp += dp[i - j][k + j];

					if (kmin == 0 || temp < kmin) kmin = temp;
				}
				dp[i][k] = kmin;

				for (int l = k; l < k + i; l++) { // 현재 파일 합치는 비용
					dp[i][k] += dp[1][l];
				}
			}
		}
		cout << dp[N][1] << "\n";
	}

	return 0;
}

 

dp[NMAX][NMAX] 각 경우에 따른 최소비용을 저장하는 배열
int i 합쳐진 파일의 개수
int k 파일은 총 'N-i+1'만큼의 묶음으로 나누어짐. 
몇번째 묶음의 최소비용을 구하는지 가리키기 위한 변수
for (int j = 1; j <= i - 1; j++) {} j를 1부터 i-1까지 증가시키면서, 현재 k가 가리키고 있는 파일 묶음의 합쳐질 수 있는 경우의 수를 고려.
다양한 경우의 비용을 계산 및 비교해 최소비용 kmin에 저장
for (int l = k; l < k + i; l++) {} dp에는 여태까지 사용된 비용을 저장하므로, 현재 파일을 합치는데 드는 비용을 따로 계산하여 더함

[comment]

dp문제는 규칙을 찾는 문제 같아서 개인적으로는 재밌는 것 같다. 다만 시간 복잡도가 N^3인 점이 찝찝해서 찾아보니 N^2으로 푸는 방법도 존재한다고 하여 공부해봐야할 것 같다.