codevs 3269 混合背包 如题(二进制优化)

3269 混合背包

 

 时间限制: 1 s
 空间限制: 256000 KB
 题目等级 : 钻石 Diamond
 
 
 
 
题目描述 Description

背包体积为V ,给出N个物品,每个物品占用体积为Vi,价值为Wi,每个物品要么至多取1件,要么至多取mi件(mi > 1) , 要么数量无限 , 在所装物品总体积不超过V的前提下所装物品的价值的和的最大值是多少?

输入描述 Input Description

第一行两个数N,V,下面N行每行三个数Vi,Wi,Mi表示每个物品的体积,价值与数量,Mi=1表示至多取一件,Mi>1表示至多取Mi件,Mi=-1表示数量无限

输出描述 Output Description

1个数Ans表示所装物品价值的最大值

样例输入 Sample Input

2 10

3 7 2

2 4 -1

样例输出 Sample Output

22

数据范围及提示 Data Size & Hint

对于100%的数据,V <= 200000 , N <= 200

------------------------------------------------------------------------------------------------------------------------

看题目就知道是混合背包模板题

这里的多重背包不能用三个for,即不能够直接把每件物品的数量展开变成01背包,会T,需要用二进制拆分优化

简单地讲一下自己对于二进制拆分的理解吧

用二进制拆分的方法,也是将多重背包转化为01背包,但我们不是一个一个地去枚一种物品的数量,而是把这个物品的数量按二进制拆分成几个数。例如物品的数量为7,体积为v,价值为w,则7的二进制为111,就可以把7拆成 001 010 100 ,即1 2 4,这样就能把这中数量为7的物品拆分成3种数量为1的物品,分别为 v1=1*v,w1=1*w ; v2=2*v,w2=2*w ; v3=4*v,w3=4*w。

为什么能这样拆呢?

因为这样拆之后的选择可以囊括所有组合

还是以上面的例子

把7(111)拆成001 010 100之后,这三个数可以组合成任意小于等于7的自然数。

详细看代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #define maxn 233333
 5 using namespace std;
 6 int read();
 7 void zeropack(int,int);
 8 void compack(int,int);
 9 void multpack(int,int,int);
10 int n,V,dp[maxn];
11 int main(){
12     n=read(),V=read();
13     for(int i=1;i<=n;i++){
14         int v=read(),w=read(),num=read();
15         if(num==1) zeropack(v,w);
16         else if(num==-1) compack(v,w);
17         else multpack(v,w,num);
18     }
19     printf("%d",dp[V]);
20     return 0;
21 }
22 void zeropack(int v,int w){
23     for(int i=V;i>=v;i--)
24     dp[i]=max(dp[i],dp[i-v]+w);
25 }
26 void compack(int v,int w){
27     for(int i=v;i<=V;i++)
28     dp[i]=max(dp[i],dp[i-v]+w);
29 }
30 void multpack(int v,int w,int num){
31     if(num*v>=V){
32         compack(v,w);
33         return;
34     }
35     int k=1;
36     while(k<num){
37         zeropack(v*k,w*k);
38         num-=k;
39         k<<=1;
40     }
41     if(num>0) zeropack(num*v,num*w);
42 }
43 int read(){
44     int ans=0,f=1;char c=getchar();
45     while('0'>c||c>'9'){if(c=='-')f=-1;c=getchar();}
46     while('0'<=c&&c<='9')ans=ans*10+c-48,c=getchar();return ans*f;
47 }
混合背包(二进制优化)
原文地址:https://www.cnblogs.com/lpl-bys/p/7648073.html