#include <iostream>
#include <algorithm>
using namespace std;
int testcase;
int fileSize[501];
int sum[501];
int dp[501][501];
const int intmax = 2100000000;
void initData() {
for (int i = 0; i < 501; i++) {
fileSize[i] = 0;
sum[i] = 0;
for (int j = 0; j < 501; j++) {
dp[i][j] = 0;
}
}
return;
}
int solve(int i, int j) {
if (i == j) return 0;
int minResult = intmax;
for (int k = 0; k < abs(i - j); k++) {
minResult = min(minResult, dp[i][i + k] + dp[i + k + 1][j] + sum[j] - sum[i - 1]);
}
return minResult;
}
int main() {
cin >> testcase;
while (testcase > 0) {
testcase--;
initData();
int fileNum;
cin >> fileNum;
for (int i = 1; i <= fileNum; i++) {
cin >> fileSize[i];
sum[i] = sum[i - 1] + fileSize[i];
}
for (int i = 0; i <= fileNum; i++) {
for (int j = 1; i + j <= fileNum; j++) {
dp[j][i + j] = solve(j, i + j);
}
}
cout << dp[1][fileNum] << endl;
}
return 0;
}
한방에 맞췄다!
처음에 인접한 파일끼리만 합칠수 있다는 걸 모르고 있을 때는,
아니 이게 시간복잡도가 N^N인거 같은데 도대체 어떻게 풀리는거지;;
라는 생각을 하면서 고민하다 잘 안풀려서 '질문' 탭을 눌렸다
그런데 거기서 '소설'이니까 인접한 파일끼리만 합칠 수 있다는 걸 봤다. (문제를 제대로 읽어야 한다..)
그걸 보는순간
그럼 배열을 양분하고 각 양분된 배열을 합치는 시간의 합이 전체 배열을 합치는 시간이구나!
라는 생각이 들었다.
DP[i][j]는 '배열의 [i...j]의 파일을 합치는 최소 시간'이다.
DP[i][j] = min(
DP[i][i+k] + DP[i+k+1][j]
(k := 0...j-1)
)
이라는 결론에 도달해서 문제를 풀었는데
예제에서 오답이 나왔다.
고민고민해보니 내가 푼건 파일을 합치는 '누적'이 아니였다.
파일을 합치는 누적된 시간을 넣으려면 각 DP[i][j]에 sum(i..j)를 넣어야 된다.
그런데 가만 생각 해보니 여기서 sum(i..j)는 부분 합이다.
따라서 sum(i..j)는 sum(0..j) - sum(0..i-1)이므로 위의 소스처럼 작성했다.
결론은
DP[i][j] = min(
DP[i][i+k] + DP[i+k+1][j] + sum(0..j) - sum(0..i-1)
(k := 0...j-1)
)
이다.
solve 함수에서 부분합을 가장 마지막에 더했으면 조금 더 최적화가 됐을듯.
'다이나믹 프로그래밍이란 부분문제와 중복된 문제가 핵심' 이란걸 기억하면 문제를 푸는 아이디어를 떠올릴 수 있을것 같다.
'코딩 > 알고리즘' 카테고리의 다른 글
백준 1912 연속합 (0) | 2017.05.27 |
---|---|
백준 2965 캥거루 세마리 (0) | 2017.05.27 |
백준 9251 LCS (0) | 2017.05.25 |
백준 1520 내리막 길 (0) | 2017.05.22 |
백준 2156 포도주 시식 (0) | 2017.05.22 |