石子歸併 51Nod
阿新 • • 發佈: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堆石子的數量,計算最小合併代價。
思路
區間dp, 首先想一下狀態轉移方程,雖然比較難想(對本菜鳥來說), dp[i][j]為第i堆石子到第j堆合併的花費
狀態轉移方程:dp[i][j]=min(dp[i][j], dp[i][k]+dp[k+1][ j ] +sum[j]-sum[i-1])
區間dp模板
//mst(dp,0) 初始化DP陣列
for(int i=1;i<=n;i++)
{
dp[i][i]=初始值
}
for(int len=2;len<=n;len++) //區間長度
for(int i=1;i<=n;i++) //列舉起點
{
int j=i+len-1 ; //區間終點
if(j>n) break; //越界結束
for(int k=i;k<j;k++) //列舉分割點,構造狀態轉移方程
{
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);
}
}
程式碼
#include <bits/stdc++.h>
using namespace std;
const int maxn=100+10;
int dp[maxn][maxn];
int sum[maxn];
int main()
{
//freopen("input.txt", "r", stdin);
int n,len,j,k, i;
cin >> n;
memset(dp, 0x3f, sizeof(dp));
for(i=1, sum[0]=0;i<=n;i++) {
cin >> sum[i];
sum[i]+=sum[i-1];
dp[i][i]=0;
}
for(len=2;len<=n;len++){ // 列舉區間長度
for(i=1;i<=n;i++){ // 列舉區間起點
j=len+i-1;
if(j>n) break;
for(k=i;k<=j-1;k++){
dp[i][j]=min(dp[i][j], dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
}
}
}
cout << dp[1][n] << endl;
return 0;
}