洛谷 P1880 石子合併 題解
阿新 • • 發佈:2018-11-04
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; }