[HEOI2013]Eden 的新背包问题

[HEOI2013]Eden 的新背包问题

题意:

多重背包,但是每一个询问会有一种物品不能选,询问最大价值。

分析:

首先进行二进制拆分是肯定的,但是这样直接算肯定不行。

我们回想起来原先 \(dp\) 状态的设定:

\(dp[i][j]\) 表示选到第 \(i\) 个物品,背包上限为 \(j\)

我们定义两个 \(dp\) 数组,一个表示选到第 \(i\) 个物品,另一个表示选到第 \(n-i\) 个物品。

计算答案时,就可以表示成 \(ans=max(dp[l][j]+dp[r+1][W-j])\)

其中,\(l\) 表示这个不能选择的物品最左端的位置 \(-1\) ,即为左侧最靠右能选择的物品。

\(r+1\) 则表示右侧最靠左能选择的物品。

分别进行计算之后就行了。

代码:

// P4095 [HEOI2013]Eden 的新背包问题
#include<bits/stdc++.h>
#define ll long long 
using namespace std;
int n,m;
const int N=1e5+5;
int w[N],v[N],cnt,id[N];
ll f1[N][1005],f2[N][1005];
int main(){
    cin>>n;
    for(int i=1,x,y,z;i<=n;i++){
        scanf("%d%d%d",&x,&y,&z); int now=1;
        while(now<=z){
            w[++cnt]=x*now,v[cnt]=y*now; 
            id[cnt]=i;
            z-=now;   now=now*2; 
        }
        if(z){
            w[++cnt]=x*z,v[cnt]=y*z; z=0;
            id[cnt]=i;
        }
    }
    for(int i=1;i<=cnt;i++){
        for(int j=0;j<=1000;j++) f1[i][j]=f1[i-1][j];
        for(int j=1000;j>=w[i];j--)
            f1[i][j]=max(f1[i][j],f1[i-1][j-w[i]]+v[i]);
    }
    for(int i=cnt;i>=1;i--){
        for(int j=0;j<=1000;j++) f2[i][j]=f2[i+1][j];
        for(int j=1000;j>=w[i];j--)
            f2[i][j]=max(f2[i][j],f2[i+1][j-w[i]]+v[i]);
    }
    cin>>m;
    for(int i=1,now,W;i<=m;i++){
        scanf("%d%d",&now,&W); now++;
        ll ans=0; int l=0,r=0;
        while(id[l+1]<now&&l<cnt) ++l; r=l;
        while(id[r+1]<=now&&r<cnt) ++r;
        for(int j=0;j<=W;j++) ans=max(ans,f1[l][j]+f2[r+1][W-j]);
        cout<<ans<<endl;
    }
    system("pause");
    return 0;
}

posted @ 2021-09-26 16:08  Evitagen  阅读(49)  评论(0编辑  收藏  举报