區間DP石子合併問題 & 四邊形不等式優化

入門區間DP,第一個問題就是線性的規模小的石子合併問題

dp數組的含義是第i堆到第j堆進行合併的最優值

就是說dp[i][j]可以由dp[i][k]和dp[k+1][j]轉移過來

狀態轉移方程 dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] + sum[i][j]) 對於第i堆到第j堆合併的花費 他的子問題是第i個的合併順序

op1:k實際上控制的是第i堆也就是起始堆的合併順序 因爲必須是相鄰合併dp[i][i] 先合併dp[i+1][j]最後再來合併第i堆 dp[i][i+1]  && dp[i+2][j] 就是分開合併i 和i + 1堆,i+2和j之間所有的堆,最後+tmp作整體合併 ok got it!

/*
入門問題——取石子
每次只能合併相鄰的一堆
求最小花費
dp[i][j] 常常用來表示i到j這個區間的最優值是多少
也就是說dp[i][j]可以由dp[i][k]和dp[k+1][j]轉移過來

狀態轉移方程
dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] + sum[i][j])
對於第i堆到第j堆合併的花費
他的子問題是第i個的合併順序
op1:k實際山控制的是第i堆也就是起始堆的合併順序
因爲必須是相鄰合併dp[i][i] 先合併dp[i+1][j]最後再來合併第i堆
dp[i][i+1]  && dp[i+2][j]
就是分開合併i 和i + 1堆,i+2和j之間所有的堆,最後+tmp作整體合併
ok got it!

*/

#include <iostream>
#include <cstdio>
#include <string.h>
#define inf 1e8 + 1e7
using namespace std;
const int maxn = 1e3 + 1e1;
int dp[maxn][maxn],sum[maxn];
int a[maxn];
//由sum[i][j]表示第i堆石子到第j堆石子全部合併的總石子量。
//dp[i][j]爲第i堆石子到第j堆合併的花費
int getMinval(int n)
{
    memset(dp,0,sizeof(dp));
    for(int v = 1;v < n;v++)//第一維:循環控制合併堆的長度從長度2開始
        for(int i = 0;i < n-v;i++)//起點
    {
        int j = i + v;//終點
        dp[i][j] = inf;//僅僅一次初始化的機會,所以可以在裏面進行初始化
        int tmp = sum[j] - (i > 0 ? sum[i - 1] : 0);//求取i-j中合併的最後一次花費
        for(int k = i;k < j;k++)
            dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] + tmp);

    }
    return dp[0][n-1];
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&a[i]);
        }
        sum[0] = a[0];
        //sum[i] 表示從1-i堆中石子的總個數
        for(int i = 1;i < n;i++)
            sum[i] = sum[i-1] + a[i];
        printf("%d\n",getMinval(n));
    }
    return 0;
}

 數據量稍微大一些的就不能用n3的複雜度來進行計算了

於是我們找到了數學優化公式——四邊形不等式優化法則

先來說結論,優化的地方是對k的尋找,最優值k

如果零s[i][j] 來表示dp[i][j]取得最優值時候的k值,那麼k的取值範圍是{s[i][j-1],s[i+1][j]}

關於證明看了很多博客了,這裏有一篇博客不錯,拿來引用一下

https://blog.csdn.net/NOIAu/article/details/72514812

核心如下

~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) 
如果不知道爲什麼,沒有關係,反正後面都要證明

 具證明過程,有興趣的可以去看看上面大佬的博客

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

對於代價數組cost[i][j]要進行證明其符合四邊形不等式:cost[i][j]+cost[i+1][j+1]<=cost[i+1][j]+cost[i][j+1]

對於所有的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] 證畢

最後來看決策函數的滿足性(s[i][j] = k)

現在我們已經證明了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 <string.h>
#define inf 1e8 + 1e7;
using namespace std;
const int maxn = 1e4 + 1e1;
int dp[maxn][maxn],s[maxn][maxn],cnt[maxn];
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(cnt,0,sizeof(cnt));
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&cnt[i]);
            cnt[i] = cnt[i-1] + cnt[i];
        }
        for(int i = 0;i <= n;i++)
        {
            dp[i][i] = 0;
            s[i][i] = i;
        }
        for(int i = n;i >= 1;i--)//從上到下進行鋪墊,因爲高決策範圍爲s[i+1][j]
        {
            for(int j = i + 1;j <= n;j++)//從下到上進行鋪墊因爲,低決策範圍需要s[i][j-1]
            {
                //有過疑問:爲什麼從i+1開始,這不廢話,直線型的dp,i到j的最優值問題,區間問題。。。
                int tmp = inf;
                int te;
                for(int k = s[i][j-1];k <= s[i+1][j];k++)//取最優決策集合//這樣的性質是這個不等式擁有的,他的最優解就是這樣單調的
                {
                    if(tmp > dp[i][k] + dp[k+1][j] + cnt[j] - cnt[i-1])
                    {
                        tmp = dp[i][k] + dp[k+1][j] + cnt[j] - cnt[i-1];
                        te = k;
                    }
                }
                dp[i][j] = tmp;
                s[i][j] = te;
            }
        }
        cout<<dp[1][n]<<endl;
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章