🔗 11066번 : 파일 합치기
● Java 풀이
● 시간 제한 2초
● 메모리 제한 256 MB
문제
소설가인 김대전은 소설을 여러 장(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 이다.
소설의 각 장들이 수록되어 있는 파일의 크기가 주어졌을 때, 이 파일들을 하나의 파일로 합칠 때 필요한 최소비용을 계산하는 프로그램을 작성하시오.
풀이
import java.io.*;
import java.util.*;
public class Main {
private static final int MAX_COUNT = 500;
private static int K; // 소설 장의 수
private static long[] files; // 각 파일의 크기
private static long[][] dp;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringBuilder sb = new StringBuilder();
StringTokenizer st;
int T = Integer.parseInt(br.readLine());
files = new long[MAX_COUNT + 2];
dp = new long[MAX_COUNT + 2][MAX_COUNT + 2];
for (int tc = 0; tc < T; tc++) {
K = Integer.parseInt(br.readLine());
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= K; i++) {
files[i] = files[i - 1] + Long.parseLong(st.nextToken()); // 파일 크기 배열 초기화
Arrays.fill(dp[i], Long.MAX_VALUE); // DP 배열 초기화
dp[i][i] = 0; // 자기 자신의 시간 비용 0
}
sb.append(findMinFileSize()).append("\n");
}
bw.append(sb.toString());
bw.flush();
bw.close();
br.close();
}
private static long findMinFileSize() {
for (int i = 1; i <= K; i++) { // 까지
for (int j = i - 1; 1 <= j; j--) { // 에서
//System.out.println("C" + j + "에서 C" + i + "까지");
if(i - j == 1) { // 하나 차이가 날 경우
dp[j][i] = Math.min(files[i] - files[j - 1], dp[j][i]); // 시간 비용은 하나 뿐
continue;
}
for (int k = j; k < i; k++) { // x(j, k) 파일과 x(k + 1, i) 파일 합치기
//System.out.println("x(" + j + ", " + k + ") 파일과 " + "x(" + (k + 1) + ", " + i + ") 파일 합치기...");
dp[j][i] = Math.min(dp[j][k] + dp[k + 1][i] + files[k] - files[j - 1] + files[i] - files[k], dp[j][i]);
}
}
}
return dp[1][K];
}
}
어제 푼 행렬 곱셈 순서와 비슷한 DP 문제.
차이점은 경우의 수가 줄어든 것?
나도 헤맸고, 다른 사람들도 쉽게 헤맨 유의할 점은
- 연속된 파일만을 합칠 수 있다. 즉, C1와 C3 같은 경우는 합치지 못한다는 것.
- 총 비용 수와 파일의 크기를 같다고 보면 안 된다.
나 같은 경우에는 2번에서 엄청나게 헤매었다.
예를 들어 문제에서 주어진 테스트 케이스가 존재한다고 가정했을 때, 계산 과정은
40 30 30 50
(30 크기 파일) + (30 크기 파일) = 현재까지 총 시간 비용 60
40 (60) 50
(40 크기 파일) + (60 크기 파일) = 현재까지 총 시간 비용 100 + 60 = 160
(100) 50
(100 크기 파일) + (50 크기 파일) = 현재까지 총 시간 비용 160 + 50 = 310
그러니까, 40 크기 파일과 60 크기 파일을 합치는 과정에서 DP[40][60]은 160이 되지만,
fileSize[40][60] 값은 100이라는 것이다.
무턱대고 파일 사이즈와 DP 값을 같은 값이라 판단한다면, 두 번째 테스트 케이스에서 1592라는 터무니 없이 큰 값이 나올 것이다.
(저도 알고싶지 않았어요)
나 같은 경우에는 파일 크기를 저장할 때 애초에 누적합으로 저장해 범위의 합을 구하는 방식으로 계산했다.
그리고 혹시 몰라서 새로운 배열 객체는 테스트 케이스 반복문 바깥에서 최대값으로 설정해주었다.
'알고리즘 및 데이터 구조 > 백준' 카테고리의 다른 글
[백준] 2146번 : 다리 만들기 (Java) (2) | 2024.02.27 |
---|---|
[백준] 1068번 : 트리 (Java) (0) | 2024.02.25 |
[백준] 11049번 : 행렬 곱셈 순서 (Java) (0) | 2024.02.23 |
[백준] 9466번 : 텀 프로젝트 (Java) (0) | 2024.02.22 |
[백준] 14890번 : 경사로 (Java) (0) | 2024.02.20 |