1. 程式人生 > >洛谷 P1880 石子合併 題解

洛谷 P1880 石子合併 題解

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堆石子的數量,計算最小和最大合併代價。

n<=100

【冷靜分析】

考慮合併的過程是怎麼樣的。

一定存在一個最後一次合併。

1k與k+1n合併。列舉k,之後變為了f[1][k],f[k+1][n]的子問題,所以dp的轉移方程就可以得出:

f[i][j]=min(f[i][k]+f[k+1][j])+sum[i]j

用一個字首和維護sum即可

​
#include<bits/stdc++.h>
using namespace std;
int ans1=0,ans2=9999999,n,a[2000],dis[1000][1000],f[1000][1000],pre[1000];
int main(){
    cin>>n;
    for ( int i=1; i <=n; i++) {
        cin>>a[i];
        a[i+n]=a[i];
}
for (int i=1;i<=2*n;++i)
pre[i]=pre[i-1]+a[i];
  for(int len=2;len<=n;len++)
    for(int i=1;i<=2*n-len+1;i++){
        int j=i+len-1;
         f[i][j]=999999,dis[i][j]=0;
        for(int k=i;k<j;k++){
                f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+pre[j]-pre[i-1]);//斷與不斷相比較
                dis[i][j]=max(dis[i][j],dis[i][k]+dis[k+1][j]+pre[j]-pre[i-1]);
        }
    }
        for (int i=1;i<=n;++i)
        {
            ans1=max(ans1,dis[i][i+n-1]);
            ans2=min(ans2,f[i][i+n-1]);
        }
    cout<<ans2<<endl;
    cout<<ans1<<endl;
    return 0;
}

​