이 문제는 언뜻 보면 작은 파일들부터 합치면 될 것 같은 '그리디(Greedy)' 문제처럼 보입니다. 하지만 인접한 파일만 합칠 수 있다는 제약 조건과, 합쳐진 파일이 다시 다른 파일과 합쳐질 때 가중치가 누적된다는 점 때문에 그리디 접근은 최적해를 보장하지 못합니다.
이 문제의 본질은 행렬 곱셈 순서(Matrix Chain Multiplication) 문제와 동일한 구간 DP (Interval DP) 유형입니다. 전체 구간 을 합치는 최소 비용은, 이를 두 개의 부분 구간 와 으로 나누어 해결한 결과의 합 중 최솟값을 찾는 방식으로 귀결됩니다. 즉, 큰 문제를 작은 부분 문제로 나누어 푸는 최적 부분 구조(Optimal Substructure)를 가집니다.
DP 테이블을 정의하고, 바텀업(Bottom-Up) 방식으로 채워 나갑니다.
구간 에서 까지 합치는 비용은, 어떤 분기점 ()에서 잘랐을 때의 좌측 비용, 우측 비용, 그리고 현재 단계에서 합치는 비용(구간합)의 합입니다.
여기서 부분은 미리 계산해 둔 누적 합 배열 를 이용해 S[j] - S[i-1]로 간단히 처리합니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer st;
static int T;
// 최솟값 비교를 위한 초기화 값 (오버플로우 방지 주의)
static final int INF = Integer.MAX_VALUE;
public static void main(String[] args) throws IOException {
T = Integer.parseInt(br.readLine());
for (int i = 0; i < T; i++) {
int K = Integer.parseInt(br.readLine());
st = new StringTokenizer(br.readLine());
int[] A = new int[K + 1];
int[] S = new int[K + 1]; // 누적 합 배열
for (int j = 1; j <= K; j++) {
A[j] = Integer.parseInt(st.nextToken());
S[j] = S[j - 1] + A[j]; // 누적 합 계산
}
solv(K, S);
}
}
private static void solv(int n, int[] sum) {
int[][] dp = new int[n + 1][n + 1];
// len: 구하려는 구간의 길이 (2개부터 n개까지)
for (int len = 2; len <= n; len++) {
// i: 구간의 시작점
for (int i = 1; i <= n - len + 1; i++) {
int j = i + len - 1; // j: 구간의 끝점
int psum = sum[j] - sum[i - 1]; // 현재 구간의 합 비용
dp[i][j] = INF; // 최소값을 구하기 위해 초기화
// k: 구간을 나누는 분기점
for (int k = i; k < j; k++) {
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j] + psum);
}
}
}
System.out.println(dp[1][n]);
}
}
이 알고리즘은 3중 반복문을 사용합니다.
따라서 전체 시간 복잡도는 입니다. 이 최대 500이므로 , 약 1.25억 연산입니다. 자바의 경우 1~2초 내에 충분히 통과 가능한 수치입니다. 만약 구간 합을 반복문으로 매번 구했다면 가 되어 시간 초과가 났을 것입니다. 누적 합(Prefix Sum)의 적용이 필수적인 이유입니다.
INF): dp[i][j]를 갱신할 때 Math.min을 사용하므로, 초기값을 충분히 큰 값(Integer.MAX_VALUE)으로 설정해야 합니다.i와 j를 단순히 증가시키는 것이 아니라, 구간의 길이(len)를 기준으로 바깥 루프를 돌려야 한다는 점이 중요합니다. 작은 구간(길이 2, 3...)의 해가 먼저 구해져 있어야 큰 구간의 해를 구할 수 있기 때문입니다.처음 문제를 접했을 때 DP의 점화식은 세웠으나, 비용 계산 부분에서 누적 합을 활용하지 않아 비효율적인 로직이 될 뻔했습니다. DP 문제에서 '구간의 합'이 빈번하게 필요할 때는 반드시 Prefix Sum을 전처리(Preprocessing) 단계에서 준비해야 함을 다시 한번 상기했습니다.