BZOJ1190: [HNOI2007]梦幻岛宝珠【分层背包】

题目描述:

给你N颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过W,且总价值最大为,并输出最大的总价值。
N<=100;W<=230,并且保证每颗宝石的重量符合a*2b(a<=10;b<=30)

题目分析:

比较完整的题解点这里

按照 b b b对物品分层,设 f [ i ] [ j ] f[i][j] f[i][j]表示可用容量为 j ∗ 2 i j*2^i j2i时已经装的最大价值。每一层继承上一层的背包,可用容量变为 j < < 1 ∣ ( W > > i & 1 ) j<<1|(W>>i\&1) j<<1(W>>i&1)

如果由高位到低位做背包,那么容量是不需要超过1000*2b的,因为后面的所有物品加起来都没有这么重,所以如果剩余容量大于1000*2b,直接存在1000的位置就好了。
所以背包时每层的容量只需要开到1000,复杂度为 O ( 1000 ∗ l o g 2 30 + 100 ∗ 1000 ) O(1000*log2^{30}+100*1000) O(1000log230+1001000)

Code:

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define maxn 105
#define maxm 1005
using namespace std;
const int Log = 30, M = 1000;
int n,W,f[Log+3][maxm];
vector<int>wt[Log+1],val[Log+1];
inline void chkmax(int &a,int b){a<b&&(a=b);}
int main()
{
    while(scanf("%d%d",&n,&W),n!=-1){
        for(int i=0;i<=Log;i++) wt[i].clear(),val[i].clear();
        for(int i=1,a,b,v;i<=n;i++){
            scanf("%d%d",&a,&v),b=0;
            while(!(a&1)) a>>=1,b++;
            wt[b].push_back(a),val[b].push_back(v);
        }
        memset(f,-0x3f,sizeof f),f[Log+1][0]=0;
        for(int i=Log;i>=0;i--){
            for(int j=0;j<=M;j++)
                chkmax(f[i][min(M,j<<1|(W>>i&1))],f[i+1][j]);
            for(int j=wt[i].size()-1;j>=0;j--)
                for(int k=0;k<=M-wt[i][j];k++)
                    chkmax(f[i][k],f[i][k+wt[i][j]]+val[i][j]);//qia hao
        }
        printf("%d\n",*max_element(f[0],f[0]+M+1));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值