1. 程式人生 > >NYOJ 石子合併(一)經典區間DP

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;

}