N명의 사람들이 한 줄로 서 있고, K개의 열차에 나누어 탑승해야 한다. 각 열차는 연속된 사람들을 태워야 하며, 각 열차의 어색함은 탑승한 사람들 간의 어색한 정도 ( u_{ij} )의 합으로 정의된다. 모든 열차의 어색함의 합을 최소화하도록 사람들을 나누는 방법을 찾아야 한다.
어색함 계산:
다이나믹 프로그래밍:
분할정복 최적화:
import java.io.*;
import java.util.*;
public class Main {
static final int INF = Integer.MAX_VALUE;
static int N, K;
static int[][] U;
static int[][] D1, D2, D, P;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
U = new int[N + 1][N + 1];
D1 = new int[N + 1][N + 1];
D2 = new int[N + 1][N + 1];
D = new int[K + 1][N + 1];
P = new int[K + 1][N + 1];
for (int i = 1; i <= N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 1; j <= N; j++) {
U[i][j] = Integer.parseInt(st.nextToken());
}
}
preprocess();
for (int i = 1; i <= N; i++) {
D[1][i] = D2[1][i];
}
for (int t = 2; t <= K; t++) {
solve(t, t, N, t - 1, N - 1);
}
System.out.println(D[K][N]);
}
static void preprocess() {
for (int i = 1; i <= N; i++) {
D1[i][1] = U[i][1];
for (int j = 2; j <= N; j++) {
D1[i][j] = D1[i][j - 1] + U[i][j];
}
}
for (int i = 1; i <= N; i++) {
D2[i][i] = 0;
for (int j = i + 1; j <= N; j++) {
D2[i][j] = D2[i][j - 1] + (D1[j][j] - D1[j][i - 1]);
}
}
}
static void solve(int t, int st, int en, int Pmn, int Pmx) {
if (st > en) return;
int mid = (st + en) / 2;
D[t][mid] = INF;
for (int sep = Pmn; sep <= Math.min(Pmx, mid - 1); sep++) {
int cost = D[t - 1][sep] + D2[sep + 1][mid];
if (cost < D[t][mid]) {
D[t][mid] = cost;
P[t][mid] = sep;
}
}
solve(t, st, mid - 1, Pmn, P[t][mid]);
solve(t, mid + 1, en, P[t][mid], Pmx);
}
}
행렬 (D1, D2) 계산:
다이나믹 프로그래밍:
분할정복 최적화:
이 코드는 분할정복 최적화를 통해 DP의 계산량을 크게 줄여 O(K⋅N⋅logN)에 문제를 해결한다. 이 문제를 풀며 어색함 합산 방식과 구간 최적화를 고려하며 효율적인 해결 방안을 설계한 점이 중요하다. 이러한 방식은 DP와 최적화가 결합된 전형적인 고급 알고리즘 문제로, 실무에서도 효율적 데이터 분할과 집계에 응용 가능하다.