1. 程式人生 > >石子歸併 51Nod

石子歸併 51Nod

題意

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; }