1. 程式人生 > 其它 >P4095 [HEOI2013]Eden 的新揹包問題 & 二進位制拆分+正反01揹包

P4095 [HEOI2013]Eden 的新揹包問題 & 二進位制拆分+正反01揹包

奇怪的題,真的很怪,調了1h

傳送門洛谷P4095 [HEOI2013]Eden 的新揹包問題

演算法要素:二進位制拆分+正反兩次01揹包(特殊01揹包)

最開始沒什麼思路,發現可以直接多重揹包,水掉50分。
打上一個二進位制拆分,資料梯度太大,因此意義不大。
考慮離線,O(1)回答詢問,結果還掉到40了。。。

正解:

step1:二進位制拆分
step2:正反兩次01揹包,dp1[i][j]表示只選前i個,dp2[i][j]表示只選n~i個。
step3:因為原先某一物品被二進位制拆分成了多個物品,去掉該物品所在區間[l,r],合併dp1[l][j],dp2[r+1][e-j]即可得到答案

細節&問題:

(1)事實說明優化和資料結構、離線不一定會起到優化的作用,方法的選擇還是要看具體情況,分析複雜度,不能認為複雜的演算法一定會有所優化
(2)關於二進位制拆分,應為val[++cnt]=aj,而非val[++cnt]=ac
(3)關於01揹包:習慣了壓縮一維總能讓我們忘記一些關鍵的性質或者是特點,比如如果不壓縮以為就需要有dp[i][j]=dp[i-1][j]這一初始化過程。
當然壓縮一維之後顯然不需要這麼初始化以下。
(4)關於多重揹包:
列舉順序為
for(i-->物品編號)
for(遞減列舉j-->體積)
for(k-->物品數量)
很容易想到,一個物品在體積之外列舉,就會被選擇多次,同樣如果j遞增列舉,也會被選擇多次,這很顯然。
而一個物品在體積之內列舉,或者遞減列舉j,該物品就不會被選擇多次。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+40,maxm=1e5+50;
int n,q,cnt;
long long dp1[maxm][maxn],dp2[maxm][maxn];
long long val[maxm],w[maxm],id[maxm];
int main()
{
    scanf("%d",&n);
    for(int i=0;i<=n-1;++i)
    {
        long long a,b,c;
        scanf("%lld%lld%lld",&a,&b,&c);
        for(long long j=1;j<=c;j<<=1)
        {
            val[++cnt]=a*j;
            w[cnt]=b*j;
            id[cnt]=i;
            c-=j;
        }
        if(c)
        {
            val[++cnt]=a*c;
            w[cnt]=b*c;
            id[cnt]=i;
        }
    }
    for(int i=1;i<=cnt;++i)
    {
        for(int j=0;j<=1000;++j) dp1[i][j]=dp1[i-1][j];
        for(int j=1000;j>=val[i];--j)
        {
            dp1[i][j]=max(dp1[i][j],dp1[i-1][j-val[i]]+w[i]);
        }
    }
    for(int i=cnt;i>=1;--i)
    {
        for(int j=0;j<=1000;++j) dp2[i][j]=dp2[i+1][j];
        for(int j=1000;j>=val[i];--j)
        {
            dp2[i][j]=max(dp2[i][j],dp2[i+1][j-val[i]]+w[i]);
        }
    }
    scanf("%d",&q);
    for(int g=1;g<=q;++g)
    {
        long long d,e;
        scanf("%lld%lld",&d,&e);
        int l=0,r=0;
        long long ans=0;
        while(l+1<=cnt && id[l+1]<d) ++l;
        r=l;
        while(r+1<=cnt && id[r+1]<=d) ++r;
        for(int j=0;j<=e;++j)
        {
            ans=max(ans,dp1[l][j]+dp2[r+1][e-j]);
        } 
        printf("%lld\n",ans);
    }
    return 0;
}