NYOJ 石子合併(一)經典區間DP
石子合併(一)
時間限制:1000 ms | 記憶體限制:65535 KB 難度:3- 描述
- 有N堆石子排成一排,每堆石子有一定的數量。現要將N堆石子併成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過N-1次合併後成為一堆。求出總的代價最小值。
- 輸入
- 有多組測試資料,輸入到檔案結束。
每組測試資料第一行有一個整數n,表示有n堆石子。
接下來的一行有n(0< n <200)個數,分別表示這n堆石子的數目,用空格隔開 - 輸出
- 輸出總代價的最小值,佔單獨的一行
- 樣例輸入
-
3 1 2 3 7 13 7 8 16 21 4 18
- 樣例輸出
-
9 239
- 來源
- 經典問題
開始以為通過貪心演算法可能很快解決問題,可是是行不通的。
首先我們可以把這麼堆石子看成一列
我們假如5堆的石子,其中石子數分別為7,6,5,7,100
•按照貪心法,合併的過程如下:
每次合併得分
第一次合併 7 6 5 7 100 =11
第二次合併 7 11 7 100=18
第三次合併 18 7 100 =25
第四次合併 25 100 =125
總得分=11+18+25+125=179
•另一種合併方案
每次合併得分
第一次合併 7 6 5 7 100 ->13
第二次合併 13 5 7 100->12
第三次合併 13 12 100 ->25
第四次合併 25 100 ->125
總得分=13+12+25+125=175
顯然利用貪心來做是錯誤的,貪心演算法在子過程中得出的解只是區域性最優,而不能保證使得全域性的值最優。
如果N-1次合併的全域性最優解包含了每一次合併的子問題的最優解,那麼經這樣的N-1次合併後的得分總和必然是最優的。
因此我們需要通過動態規劃演算法來求出最優解。
在此我們假設有n堆石子,一字排開,合併相鄰兩堆的石子,每合併兩堆石子得到一個分數,最終合併後總分數最少的。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define min(a,b) a>b? b:a
#define Maxn 0x3f3f3f3f
int dp[210][210],sum[210],a[210];
int main()
{
int n,add;
while(scanf("%d",&n)!=EOF){
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
add=0;
for(int i=0;i<n; i++){
scanf("%d",&a[i]);
add += a[i];
sum[i] = add;
}
for(int v=1;v<n;v++){
for(int i=0;i<n-v;i++){
int j= i+v;
dp[i][j] = Maxn;
add = sum[j] - (i==0? 0: sum[i-1]);
for(int k=i; k<j;k++){
dp[i][j] = min(dp[i][j] , dp[i][k]+dp[k+1][j]+add);
}
}
}
printf("%d\n",dp[0][n-1]);
}
return 0;
}