Codevs 3269-混合背包

原题

题目描述 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

题意

题目说的很民白了。。。。

题解

这道题就是01背包,完全背包,多重背包的结合,而且数据极大。【数据大得pascal调用max函数还会TLE!!调(kan)了(le)好(ti)久(jie)才发现这个问题

倘若是01背包与完全背包的结合,这道题就挺好办的,就可以这样乱搞:

for i:=1 to n do
begin
  if 01背包的情况 then for j:=m downto w[i] do f[j]:=max(f[j],f[j-w[i]]+c[i]) else
  if 完全背包的情况 then for j:=w[i] to m do f[j]:=max(f[j],f[j-w[i]]+c[i]) ;
end;

01+完全

可事实不是这样的,还多了一个多重背包。接下来就来说明一下怎么将多重背包转成01背包。

假定我们读入的第i个物品的数量为p[i],重量w[i],价值c[i],那么p[i]个物品就可以分为数量分别为1,2,4,...,2^log2p[i]的物品,不够分直接取剩余的。

比如说,当p[i]=8,w[i]=3,c[i]=5时,可以分为如下4个物品:

1.p[1]=1; w[1]=w[i]*p[1]=3*1=3; c[1]=c[i]*p[1]=5*1=5

2.p[2]=2; w[2]=w[i]*p[2]=3*2=6; c[2]=c[i]*p[2]=5*2=10

3.p[3]=4; w[3]=w[i]*p[3]=3*4=12; c[1]=c[i]*p[3]=5*4=20

4.p[4]=1; w[4]=w[i]*p[4]=3*1=3; c[4]=c[i]*p[1]=5*1=5

那么,费劲周折这样分有什么好处呢?

如果我们不这么做,那么要将它转化为01背包就必须将p[i]分成p[i]份,每份一件物品,以满足p[i]+1种取的方法(即取0个,取1个,...,取p[i]个),这样子的最终的效率为O(m*sigma(p[i]))【m为背包容量】,不用看,效率极低。

我们将p[i]分为log2p[i]份则恰好解决的这个问题,先看效率,缩至O(m*sigma(log2p[i])),接着不难发现,所分成的这log2p[i]个数恰好可以拼成0..p[i]之间任意一个数,如上面p[i]=8的例子,取0件则一件不取,取1件则取p[1]或p[4],取2件则取p[1]+p[4]或p[2],取3件则取p[1]+p[2]或p[4]+p[2],取4件则取p[1]+p[2]+p[4]或p[3],取5件则取p[1]+p[3]或p[4]+p[3],取6件,取7件,取8件我就懒得打了。。

于是将它转化为01背包后就可以套用上面的代码了。

完整程序:

 1 var w1,w,c1,c,p1,p:array[0..10000] of int64;
 2 var f:array[0..200000] of int64;
 3 var top,k,n,m:int64;
 4 var i,j:longint;
 5 function pow(x:longint):int64;//没事敲快速幂练手感
 6 begin
 7   if pow=0 then exit(1);
 8   pow:=pow(x >> 1);
 9   pow:=pow*pow;
10   if odd(x) then pow:=pow*2;
11 end;
12 begin
13   readln(n,m);
14   for i:=1 to n do readln(w[i],c[i],p[i]);
15   for i:=1 to n do
16   begin
17     if (p[i]=-1)or(p[i]=1) then//01背包或完全背包的情况
18     begin
19       inc(top);w1[top]:=w[i];c1[top]:=c[i];p1[top]:=p[i];
20       continue;
21     end;
22     for j:=0 to trunc(ln(p[i])/ln(2))+1 do//处理多重背包的情况
23     begin
24       if p[i]=0 then break;
25       k:=pow(j);
26       if p[i]>=k then
27       begin
28         p[i]:=p[i]-k;inc(top);w1[top]:=w[i]*k;c1[top]:=c[i]*k;p1[top]:=k;
29       end else
30       if p[i]<k then//处理剩余的情况
31       begin
32         k:=p[i];
33         inc(top);w1[top]:=w[i]*k;c1[top]:=c[i]*k;p1[top]:=k;
34         break;
35       end;
36     end;
37   end;
38   for i:=1 to top do//乱搞
39   begin
40     if p1[i]=-1 then for j:=w1[i] to m do begin if f[j]<f[j-w1[i]]+c1[i] then f[j]:=f[j-w1[i]]+c1[i]; end else
41     if p1[i]>0 then for j:=m downto w1[i] do begin if f[j]<f[j-w1[i]]+c1[i] then f[j]:=f[j-w1[i]]+c1[i]; end;
42   end;
43   writeln(f[m]);
44 end.

CodeVS 3269-混合背包

欢迎各位转载,若转载请注明出处,毕竟伤了一只手,单手码字不是很简单。

时间: 2024-12-23 09:33:41

Codevs 3269-混合背包的相关文章

codevs 3269 混合背包(复习混合背包)

传送门 [题目大意]给出物品的数量.-1为无限个. [思路]混合背包.... [code] #include<iostream> #include<cstdio> #include<cstdlib> using namespace std; int w[100000],v[100000],c[10000],dp[20000000]; int main() { int vv,n; scanf("%d%d",&n,&vv); for(in

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=

模板——混合背包

http://codevs.cn/problem/3269/ 3269 混合背包 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond [题目描述]Description 背包体积为V ,给出N个物品,每个物品占用体积为Vi,价值为Wi,每个物品要么至多取1件,要么至多取mi件(mi > 1) , 要么数量无限 , 在所装物品总体积不超过V的前提下所装物品的价值的和的最大值是多少? [输入描述] Input Description 第一行两个数N,V,下面N行每

HDU 3535 AreYouBusy (混合背包)

题意:给你n组物品和自己有的价值s,每组有l个物品和有一种类型: 0:此组中最少选择一个 1:此组中最多选择一个 2:此组随便选 每种物品有两个值:是需要价值ci,可获得乐趣gi 问在满足条件的情况下,可以得到的最大的乐趣是多少,如果不能满足条件就输出-1 题解:二维01背包 dp[i][j]:前i组物品我们拥有j的价值时最大可获得的乐趣 0:我们需要先把dp[i]所有赋值为负无穷,这样就只能最少选一个才能改变负无穷 1:我们不需要:dp[i][j-ci]+gi(在此组中再选一个),这样就一定最

HDU 3535 分组混合背包

http://acm.hdu.edu.cn/showproblem.php?pid=3535 题意:有n组工作,T时间,每个工作组中有m个工作,改组分类是s,s是0是组内至少要做一件,是1时最多做一件,2时随意,每项工作的描述是花费的时间和获得的快乐值,求在T时间内可获的最大快乐值. memset放错位置了,折腾老半天. 分组混合背包,有的取一件或不取,有的随意,有的最少一个 分三种情况讨论 s==0 考虑前面取过时这次取或不取,前一组取过时这次取或不取 s==1 考虑前一组取过时这次取或不取

HDU-3591 混合背包

The trouble of Xiaoqian Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2277    Accepted Submission(s): 805 Problem Description In the country of ALPC , Xiaoqian is a very famous mathematician.

01背包,完全背包,多重背包,混合背包

详见大牛背包九讲(下载地址:http://pan.baidu.com/s/1b9edXW) 1 //Class->物品种类,val->价值,room->所占空间,num->物品数量,Room->背包容量 2 3 #include<stdio.h> 4 const int maxn = 1e6; 5 6 int bag[maxn]; 7 int Room; 8 9 void zero_one_bag(int,int); //01背包 10 void complete

ZOJ 3164 Cookie Choice 分组背包 混合背包

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3181 题意: 就是混合背包加分组背包,有的物品是01背包,有的是多重背包,有的是完全背包,同时物品还有不超过8组的分组,如果在同一组则最多只能选一种.问能不能恰好地用掉D的容量,并且使所获价值最大. 分析: 开始的想法是多开一个下标,先把没有分组的做了,在0的下标,然后分组分别在组号的下标里按顺序处理,是什么背包就用什么做法,不过一直WA,对拍小数据大数据都没啥问题(可能随机

蒟蒻吃药计划-治疗系列 #round4 多重背包+混合背包代码存放

1 #include <bits/stdc++.h> 2 #define fp(i,l,r) for(register int i=(l);i<=(r);++i) 3 #define fd(i,l,r) for(register int i=(l);i>=(r);--i) 4 using namespace std; 5 int v[1000+20],w[1000+20],s[1000+20]; 6 int dp[1000+20]; 7 int n,m; 8 inline int