1. 程式人生 > >四邊形不等式優化講解(詳解)

四邊形不等式優化講解(詳解)

累加器傳送門:

本篇博文意在詳細講解如下內容

F. 什麼是四邊形不等式

S. 四邊形不等式優化如何證明

T. 怎麼用四邊形不等式優化

(好氣啊,這篇博文我寫了兩遍,第一遍的沒有儲存就關了)
(感謝部落格園的Staginner,他的部落格對我有很大影響)
(感謝wys大佬親自為我查了一部分內容的錯)
(如果本文有什麼錯誤的話,請向我提出,非常感謝)
這是他的部落格:
http://www.cnblogs.com/staginner/archive/2012/03/12/2391925.html

引入:

在dp問題中,我們經常遇見這樣的一類問題
他們的dp轉移方程是這樣的

dp[i][j]=min{dp[i][k]+dp[k+1][j]+cost[i][j]}

顯然for一邊i,for一邊j,在算dp[i][j]的時候還得for一遍k,這是O(n^3)的複雜度,這樣的複雜度在很多時候是不能接受的,如果dp轉移方程已經設計好了,也無法再在dp方程上優化,我們怎麼來提高計算效率呢?(關於O(n^2)複雜度的證明我放在最後)

~PART ONE 交叉小於包含

對於( a < b <= c< d )

如果有f[a][c]+f[b][d]<=f[b][c]+f[a][d]

(可以理解為一句話,交叉小於包含,即交叉的兩個區間,a到c和b到d的值滿足小於等於包含的兩個區間[bc包含於ad])
則說這個東西滿足四邊形不等式,當然這個東西可能是dp陣列,也可以是其他陣列,比如引入裡提到的cost陣列,表示的是i到j的花費(比如合併石子問題)

給出兩個定理:

1、如果上述的w函式同時滿足區間包含單調性和四邊形不等式性質,那麼函式dp也滿足四邊形不等式性質
我們再定義s(i,j)表示 dp(i,j) 取得最優值時對應的下標(即 i≤k≤j 時,k 處的 dp 值最大,則 s(i,j)=k此時有如下定理
2、假如dp(i,j)滿足四邊形不等式,那麼s(i,j)單調,即 s(i,j)≤s(i,j+1)≤s(i+1,j+1)
如果不知道為什麼,沒有關係,反正後面都要證明

~PART TWO 證明過程三步走

(這一部分如果沒有完全懂沒有關係,PART-THREE裡會結合題目來推導一遍,所以有一小部分推導在這裡先不寫出來,這一部分儘量先說原理和方法)

壹:證明cost為凸(滿足四邊形不等式)

顯然,當且僅當對於所有的i,j,均滿足如下式子
(令i< i+1<= j< j+1)
cost[i][j]+cost[i+1][j+1]<=cost[i][j+1]+cost[i+1][j]
(利用PART ONE的交叉小於包含寫出來的式子)
轉移一下式子
即要證明對於所有的i,j,要使
cost[i][j]-cost[i+1][j]<=cost[i][j+1]-cost[i+1][j+1]
試想一個函式f(j)=cost[i][j]-cost[i+1][j],那麼要證明這個四邊形不等式成立,也就是要證明這個函式f(j)單調遞增嘛

貳:證明dp為凸(滿足四邊形不等式)

顯然我們需要證明對於任意的i,j
i< i+1<= j< j+1
均滿足
dp[i][j]+dp[i+1][j+1]<=dp[i+1][j]+dp[i][j+1]
這一步其實是建立在的上面的,可以用的結論來推導貳的結論
假設式子右邊的dp[i+1][j]取得最優值的時候的k為x(之前不是要for一遍k來尋找最優時候的解嘛),右邊式子裡的dp[i][j+1]取得最優值的時候的k為y,這個時候把k=x帶入左式的dp[i][j]中,把k=y帶入左式的dp[i+1][j+1]中,然後尋找dp陣列和cost陣列的聯絡,把左邊的式子配湊出右邊的式子然後完成證明
(可能看不懂這句話是什麼意思,不是很擅長表達,具體的證明方式大家可以結合PART-THREE裡詳細論證過程來學習,如果沒懂就很不舒服的,現在就可以結合PART THREE看啦)

叄:證明決策單調(以找min值為例)

如果我們用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]來說,我們先令dp[i][j-1]取得最優解的時候的k值為y,然後令除了最優值以外的其他值可以為x,這裡我們由於要討論單調性,所以讓x小於y,即x<=y<=j-1< j
然後利用我們在壹和貳裡已經證明了的結論,來列一個關於dp陣列的四邊形不等式,然後在兩邊同時加上cost陣列以求拼湊出如下的結論
dp[i][j-1] (k=x)+dp[i][j] (k=y)<=dp[i][j-1] (k=y)+dp[i][j] (k=x)
( 這個看起來也有點奇怪,不過PART-THREE會講具體是怎樣操作的 )
然後進行移項
dp[i][j-1] (k=x)-dp[i][j-1] (k=y)<=dp[i][j] (k=x)-dp[i][j] (k=y)
可以這樣理解
我們之前是令dp[i][j-1]取得最優值的時候k=y,所以對於所有的x小於y,一定有dp[i][j-1](k=x)>=dp[i][j-1] (k=y)(因為我們這裡最優值是指最小值),所以很自然的,dp[i][j] (k=x)-dp[i][j] (k=y)也必然會大於等於零,也就是說,有如下結論
dp[i][j] (k=x)>=dp[i][j] (k=y)
什麼意思呢,讓dp[i][j-1]可以取得最小值的y,對於dp[i][j]的決策來說,可以使得所有小於y的x的值都沒有y優,所以dp[i][j]的最優決策一定不會小於y,所以如果我們用s[i][j]表示dp[i][j]取得最優值的時候的k值,那麼一定有
s[i][j-1]<=s[i][j]
而s[i][j]<=s[i+1][j]的證明是類似的,讀者可以自己再推一遍

~PART THREE

終於到了激動人心的詳細證明過程和如何使用四邊形不等式進行優化,亦可賽艇!
看例題來講解總是好的,這裡我們就舉一個大家都做過的廣為熟知的題

合併石子問題

現在有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陣列是字首和
for一遍i,for一遍j,每算一次dp[i][j]還要for一遍k,自然是O(n^3)方,現在我們來按照規則判斷是否可以用四邊形優化

第一步(壹)證明cost為凸

對於所有的i,j,令其滿足i< i+1<=j< j+1
我們需要證明
cost[i][j]+cost[i+1][j+1]<=cost[i+1][j]+cost[i][j+1]
移項
cost[i][j]-cost[i+1][j]<=cost[i][j+1]-cost[i+1][j+1]
令f(j)=cost[i][j]-cost[i+1][j]
f(j)=cnt[j]-cnt[i-1]-(cnt[j]-cnt[i])
f(j)=cnt[i]-cnt[i-1]
都跟j無關了,自然一定滿足四邊形不等式(這個時候是直接等於了,但沒有違反四邊形不等式)

第二步(貳)證明dp為凸

要推導dp[i][j]的凸性,自然要滿足對任意的i,j,令i< i+1<=j< j+1
有如下結論
dp[i][j]+dp[i+1][j+1]<=dp[i+1][j]+dp[i][j+1]
令dp[i+1][j]取得最優值的時候k=x
令dp[i][j+1]取得最優值的時候k=y
令x < =y(之後還要令x > y,這裡不再贅述,讀者如有興趣可以自行推導,方式相似)
將k=x代入dp[i][j],k=y代入dp[i+1][j+1]
左式=dp[i][x]+dp[x+1][j]+cost[i][j]+dp[i+1][y]+dp[y+1][j+1]+cost[i+1][j+1]①
而對於i< i+1<=j< j+1
由於已經在壹中證明了cost的凸性,所以
cost[i][j]+cost[i+1][j+1]<=cost[i+1][j]+cost[i][j+1]②
我們會發現這個不等式的左邊在①式中出現過,所以把②式中的左式和右式替換一下可以得到如下結論
dp[i][x]+dp[x+1][j]+cost[i][j]+dp[i+1][y]+dp[y+1][j+1]+cost[i+1][j+1]
<=

dp[i][x]+dp[x+1][j+1]+cost[i][j+1]+dp[i+1][y]+dp[y+1][j]+cost[i+1][j]

即dp[i][j]+dp[i+1][j+1]<=dp[i][j+1]+dp[i+1][j]
證畢

第三步(叄)證明決策單調

現在我們已經證明了cost陣列和dp陣列的凸性,要證明決策單調以證明優化的正確性
即要證明s[i][j-1]<=s[i][j]<=s[i+1][j]
對於s[i][j-1]<=s[i][j]
令dp[i][j-1]取得最小值時的k=y,對於所有x≠y,令x<=y
可以有如下推導
∵x+1<=y+1<=j-1< j
四邊形不等式有:
dp[x+1][j-1]+dp[y+1][j]<=dp[y+1][j-1]+dp[x+1][j]

在式子兩邊同時加上dp[i][x]+cost[i][j-1]+dp[i][y]+cost[i][j] 可以得到

dp[i][x]+dp[x+1][j-1]+cost[i][j-1]+dp[i][y]+dp[y+1][j]+cost[i][j]
<=
dp[i][x]+dp[x+1][j]+cost[i][j]+dp[i][y]+dp[y+1][j-1]+cost[i][j-1]

dp[i][j-1]+dp[i][j]<=dp[i][j]+dp[i][j-1]
(k=x)…………(k=y)……(k=x)……(k=y)
移項

dp[i][j-1]-dp[i][j-1]<=dp[i][j]-dp[i][j]
(k=x)…………(k=y)……(k=x)……(k=y)

由於我們是令k=y時dp[i][j-1]取得最小值,那麼dp[i][j-1] (k=x)一定大於等於dp[i][j-1] (k=y),所以左式大於零,所以右式也大於零,所以對於dp[i][j-1]可以取到最優值的y,所有小於它的值,對於dp[i][j]來說,都沒有y優,所以最優決策一定不是小於y的,如果令s[i][j]表示dp[i][j]取得最優值的時候的k值,那麼一定有
s[i][j-1]<=s[i][j]
證畢
對於不等式後半部分的證明類似,讀者有興趣可以自己再證明一次

程式碼如下

#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 10000+10
using namespace std;

int dp[MAXN][MAXN],s[MAXN][MAXN],cnt[MAXN];
int n;

void init(){
    scanf("%d",&n);
    for(register int i=1;i<=n;i++) scanf("%d",&cnt[i]),cnt[i]=cnt[i-1]+cnt[i];
    for(register int i=1;i<=n;i++){
        dp[i][i]=0;s[i][i]=i; 
    }
    for(register int i=n;i>=1;i--){
        for(register int j=i+1;j<=n;j++){
            int temp=0x7fffffff;
            int te;
            for(int k=s[i][j-1];k<=s[i+1][j];k++){
                if(temp>dp[i][k]+dp[k+1][j]+cnt[j]-cnt[i-1]){
                    temp=dp[i][k]+dp[k+1][j]+cnt[j]-cnt[i-1];
                    te=k;
                }
            }
            dp[i][j]=temp;
            s[i][j]=te;
        }
    }
}

void print(){
    cout<<dp[1][n]<<endl;
}

int main(){
    init();
    print();
}

尤其要注意for的順序,因為我們在推dp[i][j]的時候要用s[i][j-1]和s[i+1][j]所以i一定是倒序的,這樣才能在求dp[i][j]的時候呼叫dp[i+1][j]的最優決策s[i+1][j],而j一定是順序的,這樣才能在求dp[i][j]的時候呼叫dp[i][j-1]的最優決策s[i][j-1]

關於O(n^2)複雜度的證明

其實證明很簡單,對於一個i,j來說,我們要for s[i][j-1]到s[i+1][j]個數,那麼所有的i和j加起來一共會for多少次呢?
我們可以這樣思考
(s[2][2]-[1][1])+(s[3][3]-s[2][2])+(s[4][4]-s[3][3])+…+(s[n][n]-s[n-1][n-1])=s[n][n]-s[1][1]很顯然是小於n的嘛,所以本來是(n *n *n)的複雜度,就這樣降成了O(n *n)啦

最後說一句

對於dp轉移合法的證明,其實很多時候我們不用像我寫的這樣去判斷,直接打表就行了,比如先跑一個O(n^3)的程式碼,跑的時候判斷是否滿足四邊形不等式,決策是否單增等等,如果不滿足就輸出false之類的,或者打一個決策表出來觀察,這樣其實會省下一部分時間,之前的所有證明,只是幫助你理解該演算法(該優化)的正確性

附加習題: