【51Nod 1021 】石子歸併 【區間DP】
阿新 • • 發佈:2019-01-27
N堆石子擺成一個環。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的代價。計算將N堆石子合併成一堆的最小代價。
例如: 1 2 3 4,有不少合併方法
1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)
括號裡面為總代價可以看出,第一種方法的代價最低,現在給出n堆石子的數量,計算最小合併代價。
Input
第1行:N(2 <= N <= 1000)
第2 - N + 1:N堆石子的數量(1 <= A[i] <= 10000)
Output
輸出最小合併代價
Input示例
4
1
2
3
4
Output示例
19
分析: dp[i][j] 表示 區間[i, j] 進行合併的最小价值,然後我們會發現,大區間的劃分可以有好多種,而且每種都是可以用小區間來表示的,所以我們可以DP;
關鍵: 用小區間來遞推大區間。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = (int) 100 + 11;
const int M = (int) 10000 + 11 ;
const int MOD = (int) 1e9 + 7 ;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int val[N];
int dp[N][N], sum[N][N];
int main(){
int n; scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &val[i]);
}
for(int i = 1; i <= n; i++){
for(int j = i; j <= n; j++){
sum[i][j] = sum[i][j - 1 ] + val[j];
}
}
memset(dp, 0x3f, sizeof(dp) );
for(int i = 0; i <= n; i++) dp[i][i] = 0;
for(int len = 2; len <= n; len++){ // 列舉區間長度len
for(int j = 1; j + len - 1 <= n; j++){ // 列舉所有長度為len的區間[L, R]
int L = j, R = L + len - 1;
for(int k = L; k < R; k++){ // 對於每個區間都可以劃分為len - 1個方案數
dp[L][R] = min(dp[L][R], dp[L][k] + dp[k + 1][R] + sum[L][R]);
}
}
}
printf("%d\n", dp[1][n]);
return 0;
}