NYOJ 737 石子合併(一)(區間dp)
阿新 • • 發佈:2019-02-13
石子合併(一)
時間限制: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
-
思路: 很基礎的區間dp dp[i][j] 表示合併i到j的所有的石子所需要花費的最小精力,假如 合併1 2 3 就有兩種合併方式:
-
第一: 先合併 1 2 在合併 12 3 所需要花費的精力是dp[1][2]+dp[3][3]+sum[1][3] (sum[i][j] 表示 從i到j 的石子總數,也是需要花
-
費 的精力) 而dp[1][2]又用什麼表示呢 ? dp[1][2]=dp[1][1]+dp[2][2]+sum[1][2]..
-
第二: 省略。。。
-
所以我們推出了 狀態轉移方程。。dp[i][j]=min(dp[i][j],dp[i][k]+dp[K+1][j]+sum[i][j]);
-
程式碼:
-
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #define N 205 #define inf 0x3f3f3f using namespace std; int dp[N][N]; int a[N]; int sum[N][N]; int he[N]; int n; int main() { int i,j,k,d; while(~scanf("%d",&n)) { memset(he,0,sizeof(he)); memset(sum,0,sizeof(sum)); for(i=1;i<=n;i++) { scanf("%d",&a[i]); he[i]=he[i-1]+a[i]; } for(i=1;i<=n;i++) { for(j=i;j<=n;j++) { sum[i][j]=he[j]-he[i-1]; } } memset(dp,inf,sizeof(dp)); for(i=1;i<=n;i++) dp[i][i]=0; for(d=1;d<n;d++) { for(i=1;i<=n-d;i++) { for(k=i;k<i+d;k++) { dp[i][i+d]=min(dp[i][i+d],dp[i][k]+dp[k+1][i+d]+sum[i][i+d]); } } } printf("%d\n",dp[1][n]); } return 0; }