1. 程式人生 > >dp優化-四邊形不等式(模板題:合併石子)

dp優化-四邊形不等式(模板題:合併石子)

看了好久,這裡整理一下證明

方程形式:dp(i,j)=min(dp(i,k)+dp(k+1,j))+cost(i,j)  O(n^3)

四邊形不等式:將其優化為O(n^2)

1.四邊形不等式

a<b<=c<d

f(a,c)+f(b,d)<=f(b,c)+f(a,d)交叉小於包含

則對於i<i+1<=j<j+1

f(i,j)+f(i+1,j+1)<=f(i+1,j)+f(i,j+1)

f(i,j)-f(i+1,j)<=f(i,j+1)-f(i+1,j+1)

令g(j)=f(i,j)-f(i+1,j),則g(j)遞增

2.若cost(i,j)有凸性,則dp(i,j)也有凸性

只需要證明對任意i<i+1<=j<j+1,有dp(i,j)+dp(i+1,j)<=dp(i+1,j)+dp(i,j+1)

設dp(i+1,j)取最優解的k=x,dp(i,j+1)取最優解的k=y

即要證:dp(i,j)+dp(i+1,j)<=dp(i,y)+dp(y+1,j+1)+dp(i+1,x)+dp(x+1,j)+cost(i,j+1)+cost(i+1,j)(1)

令A=dp(i,y)+dp(y+1,j+1)+dp(i+1,x)+dp(x+1,j);

由於dp(i,j)的最優解不一定為k=x,則dp(i,j)<=dp(i,x)+dp(x+1,y)+cost(i,j)

同理由於dp(i+1,j)的最優解不一定為k=y,則dp(i+1,j)<=dp(i+1,y)+dp(y+1,j)+cost(i+1,j)

(1)式可化為:

dp(i,j)+dp(i+1,j)<=A+cost(i,j)+cost(i+1,j)<=A+cost(i,j+1)+cost(i+1,j)

由cost的凸性可知成立,故dp(i,j)也有凸性

3.證明決策的單調性

設s(i,j)表示dp(i,j)最優時的k值

要證:s(i,j-1)<=s(i,j)<=s(i+1,j)

先證s(i,j-1)<=s(i,j):

設y=s(i,j-1),對任意x滿足x<=y<j-1<j,有x+1<=y+1<=j-1<j

由四邊形不等式:dp(x+1,j-1)+dp(y+1,j)<=dp(x+1,j)+dp(y+1,j-1)

令A=dp(i,x)+cost(i,j-1)+dp(i,y)+cost(i,j)

兩邊同時加上A

化簡得到:dp(i,j-1)(k=x時的值)+dp(i,j)(k=y)<=dp(i,j)(k=x)+dp(i,j-1)(k=y)

移項:dp(i,j-1)(k=x)-dp(i,j-1)(k=y)<=dp(i,j)(k=x)-dp(i,j)(k=y)

由於k=y時dp(i,j-1)取最小值,則左邊>=0,即dp(i,j)(k=x)>=dp(i,j)(k=y)

也就是說,對於dp(i,j),任意一個k=x<y都不如k=y優。

故s(i,j-1)<=s(i,j)

另一邊同理(不證了)

那麼關鍵就在於列舉k的部分

 1 //s(i,j-1)<=s(i,j)<=s(i+1,j)
 2     for(int i=n;i>=1;i--)
 3         (int j=i+1;j<=n;j++)
 4         {
 5             int d=INF,id=0;
 6             for(int k=s[i][j-1];k<=s[i+1][j];k++)
 7             {
 8                 if(d>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
 9                 {
10                     d=dp[i][k]+dp[k][j]+sum[j]-sum[i-1];
11                     id=k;
12                 }
13             }
14             dp[i][j]=d;
15             s[i][j]=id;
16         }

總複雜度O(n^2)

補充一個證明

對於固定的區間長度len,有

  dp[i][i+len]的決策範圍為s[i][i+len-1]至s[i+1][i+len]

  dp[i+1][i+len+1]的決策範圍為s[i+1][i+len]至s[i+2][i+len+1]

  dp[i+2][i+len+2]的決策範圍為s[i+2][i+len+1]至s[i+3][i+len+2]

  如此腦補下去,我們發現,對於固定的區間長度len,總共的決策只有O(n)個!因為一共有O(n)個不同的區間長度len,所以演算法的總複雜度就是O(n^2)!

模板題:合併石子

現在有n堆石子,要將石子按一定順序地合成一堆,規定如下,每次只能移動相鄰的兩堆石子,合併費用為新和成一堆石子的數量,求把n堆石子全部合併到一起所花的最少或者最大花費

很容易想到這樣一個dp轉移 
dp[i][j]=min{dp[i][k]+dp[k+1][j]}+cost[i][j] 
震驚!這不就是之前所講的模型嘛?原來之前O(n^3)方的合併石子問題還可以優化(我太弱了) 
首先明確一點,cost[i][j]表示把第i堆到第j堆的石子和到一起的最後一步的代價,顯然,之前無論怎麼合併,最後一步的代價都是一樣的,所以我們可以先預處理出這個cost陣列,他等於cnt[j]-cnt[i-1],其中cnt陣列是字首和 
--------------------- 
作者:NOIAu 
來源:CSDN 
原文:https://blog.csdn.net/noiau/article/details/72514812 

分析題目:

只要證明cost(i,j)滿足凸性。g(j)=cost(i,j)-cost(i+1,j)=sum(j)-sum(i-1)-sum(j)+sum(i)=sum(i)-sum(i-1)與j無關,滿足(此時為等於)。

我的模板:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N=10010,INF=(int)1e9;
 5 int dp[N][N],s[N][N],sum[N];
 6 
 7 int main()
 8 {
 9     //freopen("a.in","r",stdin);
10     int n;
11     scanf("%d",&n);
12     sum[0]=0;
13     for(int i=1;i<=n;i++)
14     {
15         int x;
16         scanf("%d",&x);
17         sum[i]=sum[i-1]+x;
18     }
19     for(int i=1;i<=n;i++) dp[i][i]=0,s[i][i]=i;
20     //s(i,j-1)<=s(i,j)<=s(i+1,j)
21     for(int i=n;i>=1;i--)
22         (int j=i+1;j<=n;j++)
23         {
24             int d=INF,id=0;
25             for(int k=s[i][j-1];k<=s[i+1][j];k++)
26             {
27                 if(d>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
28                 {
29                     d=dp[i][k]+dp[k][j]+sum[j]-sum[i-1];
30                     id=k;
31                 }
32             }
33             dp[i][j]=d;
34             s[i][j]=id;
35         }
36     printf("%d\n",dp[1][n]);
37     return 0;
38 }