김대전은 소설을 여러 개의 장으로 나누어 파일에 저장한다. 그는 모든 파일을 하나로 합치려 하는데, 두 개의 파일을 합칠 때 드는 비용은 두 파일의 크기의 합이다.
각각의 장들이 수록된 파일 크기가 주어졌을 때, 최소 비용으로 모든 파일을 합치는 방법을 찾아야 한다.
예를 들어 파일 크기가 [40, 30, 30, 50]
인 경우:
제약 조건:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void solve(int n, int[] v) {
int[][] dp = new int[n + 1][n + 1];
int[][] K = new int[n + 1][n + 1];
int[] s = new int[n + 1];
// Prefix sum 계산
for (int i = 1; i <= n; i++) {
s[i] = s[i - 1] + v[i];
}
// 초기화
for (int i = 1; i <= n; i++) {
dp[i - 1][i] = 0;
K[i - 1][i] = i;
}
// DP와 Knuth Optimization
for (int m = 2; m <= n; m++) {
for (int i = 0; i + m <= n; i++) {
int j = i + m;
dp[i][j] = Integer.MAX_VALUE;
for (int k = K[i][j - 1]; k <= K[i + 1][j]; k++) {
int cost = dp[i][k] + dp[k][j] + s[j] - s[i];
if (dp[i][j] > cost) {
dp[i][j] = cost;
K[i][j] = k;
}
}
}
}
System.out.println(dp[0][n]);
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int t = Integer.parseInt(br.readLine());
while (t-- > 0) {
int n = Integer.parseInt(br.readLine());
int[] v = new int[n + 1];
StringTokenizer st = new StringTokenizer(br.readLine());
for (int i = 1; i <= n; i++) {
v[i] = Integer.parseInt(st.nextToken());
}
solve(n, v);
}
}
}
for (int i = 1; i <= n; i++) {
s[i] = s[i - 1] + v[i];
}
for (int i = 1; i <= n; i++) {
dp[i - 1][i] = 0;
K[i - 1][i] = i;
}
for (int m = 2; m <= n; m++) {
for (int i = 0; i + m <= n; i++) {
int j = i + m;
dp[i][j] = Integer.MAX_VALUE;
for (int k = K[i][j - 1]; k <= K[i + 1][j]; k++) {
int cost = dp[i][k] + dp[k][j] + s[j] - s[i];
if (dp[i][j] > cost) {
dp[i][j] = cost;
K[i][j] = k;
}
}
}
}
이 문제는 파일을 합칠 때 발생하는 비용을 최소화하는 최적화 문제로, DP와 Knuth Optimization을 활용해 해결한다.
처음에는 DP만으로 문제를 풀었으나, 𝑂(𝑛3) 시간 복잡도로는 큰 입력을 처리할 수 없었다.
Knuth Optimization을 적용해 효율성을 극대화했으며, 문제 해결에 대한 논리적 최적화의 중요성을 다시금 느꼈다.