1. 程式人生 > >51Nod 1022 石子歸並 V2(區間DP+四邊形優化)

51Nod 1022 石子歸並 V2(區間DP+四邊形優化)

增加 pre 分享 ems 滿足 log 如果 算法 技術

題目鏈接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1022

題目大意:

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增加到了1000,原本O(n^3)的算法肯定是會超時的。所以需要用四邊形不等式優化將復雜度降為O(n^2),並且將數組倍增把環變為鏈。

粗略介紹一下四邊形優化的作用,具體證明看這裏《動態規劃加速原理之四邊形不等式》。 我們原本的狀態轉移方程為dp[i][j]=min{dp[i][k]+dp[k+1][j]+w[i][j]}(i<j,i<=k<=j)。 上式在動態規劃的狀態轉移方程中是很常見的,對於上式中的w(i,j)
如果符合w(i`,j) <= w(i,j`) i<i`<j<j` 那麽我們稱函數w滿足關於區間包含的單調性。

如果符合w(i,j)+w(i`,j`) <= w(i`,j)+w(i,j`) 那麽我們稱函數w滿足四邊形不等式。 那麽就可以使用兩個定理(圖片來源) 技術分享

於是,我們可以使用s[i][j]記錄使得dp[i][j]最優的分割點(k點),並且滿足s[i][j-1]<=s[i][j]<=s[i+1][j+1],那麽我們的k的枚舉範圍就是s[i][j-1]<=s[i][j]<=s[i+1][j+1]。

復雜度證明:

技術分享

代碼:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4
#include<algorithm> 5 using namespace std; 6 const int N=2e3+5; 7 const int INF=0x3f3f3f3f; 8 9 int dp[N][N],s[N][N],sum[N],a[N];//s[i][j]為使dp[i][j]最優的分割點 10 11 int main(){ 12 int n; 13 scanf("%d",&n); 14 memset(dp,0x3f,sizeof(dp)); 15 for(int i=1;i<=n;i++){ 16 scanf("%d",&a[i]); 17 a[i+n]=a[i]; 18 } 19 for(int i=1;i<=2*n;i++){ 20 dp[i][i]=0; 21 sum[i]=sum[i-1]+a[i]; 22 s[i][i]=i; 23 } 24 for(int d=1;d<n;d++){ 25 for(int i=1;i<=2*n-d;i++){ 26 int j=i+d; 27 for(int k=s[i][j-1];k<=s[i+1][j];k++){ 28 int tmp=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]; 29 if(tmp<dp[i][j]){ 30 dp[i][j]=tmp; 31 s[i][j]=k; 32 } 33 } 34 } 35 } 36 int ans=INF,d=n-1; 37 for(int i=1;i<=2*n-d;i++){ 38 int j=i+d; 39 ans=min(ans,dp[i][j]); 40 } 41 printf("%d\n",ans); 42 return 0; 43 }

 

51Nod 1022 石子歸並 V2(區間DP+四邊形優化)