1. 程式人生 > >dp優化1——sgq(單調隊列)

dp優化1——sgq(單調隊列)

情況 ring algo div += bsp 超過 有時 數據

該文是對dp的提高(並非是dp入門,dp入門者請先參考其他文章)

有時候dp的復雜度也有點大。。。會被卡。技術分享圖片

這幾次blog大多數會講dp優化。

回歸noip2017PJT4.(題目可以自己去百度)。就是個很好的案例。那題是個二分套dp如果dp不優化復雜度O(n^2logn)還能拿60分(CCF太仁慈了,如果是我直接給10分)。

正解加上個單調隊列(其實是sliding window)O(nlogn)

我們發現,此類dp是這樣的

技術分享圖片

狀態i是由[l,r]轉移過來的。且i在向右移動的過程中,[l,r]一定會跟著往右移,那不就是單調隊列嗎!!!

至於單調隊列都不會的,我在這給一句解釋———如果一個人比你小,還比你強,那你就永遠比不過他了--chen_zhe大佬

技術分享圖片

其實是這樣的——能轉移到i的窗口[l,r]在向右移動的過程中,我們加一個隊列,隊首的dp值最優,在r向右移動時,遇到一個狀態t

寫個偽代碼

while(隊列不空&&t的dp值由於隊尾值)彈出隊尾元素;將t插入隊尾

別忘了,l還要向右移動,右移會導致一些狀態離開隊列,需要在原隊列刪除。

OK接下來看例題:

多重背包n個物體,每個numi個,每個物品右價值和重量,求重量不超過m的最大價值(不會o(n^2m)請自行百度,改文不介紹過於基礎的dp)。

您會說一句,這種水題我30s切。結果切完後就30分。。。。

一拍腦袋,二進制優化-》O(nmlogn)(將numi分解二進制,再用01做)

結果毒瘤的數據結構大師lxl成功卡掉了您的log(送你《涼涼》x1)

看來只能用O(nm)的做法,先寫下dp轉移方程

dp[i][j]表示前i個物體,限制重量為j的最大價值

dp[i][j]=max(dp[i-1][j-k*w[i]]+v[i]*k)(0<=k<=num[i])

狀壓:dp[j]=max(dp[j-k*w[i]]+v[i]*k)

我們先瞎搞:

在i和j都確定的情況下:

設:

n*w[i]+p=j p=j%w[i];

j/w[i]=n(註意是整除)

原方程變為dp[j]=max(dp[j%w[i]+k*w[i]]-k*w[i])+n*w[i](2)

聰明的你一定會了。

這個方程的k與原來的K不同(為區分下文將原來的K大寫)

如果你自己推過(2),您會發現k=n-K

這樣可以搞出k的範圍

0<=n-k<=num[i]

n-num[i]<=k<=n

在j%w【i】不變時max(dp[j%w[i]+k*w[i]]-k*w[i])只與k有關,爽歪歪~~ 單調隊列嘍。

不懂再想想這張圖(important)

技術分享圖片

沒例題總不行!!例題是hdu1171(多重背包裸題)。但出題者非常善良,O(n^2m)也給過了。

技術分享圖片

單調隊列79MS,純dp1092MS

代碼:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int dp[2][250011],v[500011],num[500011];
int q[10000000],l,r,n;
int main(){//freopen("in.txt","r",stdin);freopen("o1.txt","w",stdout);
    while(scanf("%d",&n)!=EOF && n>=0){
        memset(dp,0,sizeof(dp));memset(v,0,sizeof(v));memset(num,0,sizeof(num));
        l=r=0;
        int i,sum=0,j;
        for(i=1;i<=n;++i)scanf("%d%d",&v[i],&num[i]),sum+=v[i]*num[i];
        int m=sum>>1,p,ans=0;
        for(i=1;i<=n;++i){
            for(p=0;p<v[i];++p){
                int kl,kr;
                l=r=0;l=1;q[++r]=0;
                for(j=p;j<=m;j+=v[i]){
                    int pre=kr;
                    kl=max(j/v[i]-num[i],0),kr=j/v[i];
                    while(l<=r && (q[l]<kl || q[l]>kr))++l;
                    for(int k=pre+1;k<=kr;++k){
                        while(l<=r && dp[i%2^1][j%v[i]+q[r]*v[i]]-q[r]*v[i]<dp[i%2^1][j%v[i]+k*v[i]]-k*v[i])--r;
                        q[++r]=k;
                    }
                    dp[i%2][j]=dp[i%2^1][j%v[i]+q[l]*v[i]]-q[l]*v[i]+(j/v[i])*v[i];
                    ans=max(ans,dp[i%2][j]);
                }
            }
        }
        printf("%d %d\n",sum-ans,ans);
    }
}

dp優化1——sgq(單調隊列)