void-man

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

0-1背包,就是每个物品只有选或者不选两种情况,且只能选一次,普通背包就是每个物品可以有多种

今天在重新回过头研究这个问题时候发现,这两种背包问题的代码区别就在于

0-1背包在写转移方程时候要从大往小写:i=0-n,j=max-w[i] bag[j]=max( bag[j-w[i]]+v[i], bag[ j ] );

这样一来,由于是从大往小的写,所以物品每次从小的添加此物品,看是否能够比现在的大,大的话就加上赋值给当前大的重量,此时小的里面还不含此物品,那么背包就不可能把每个物品使用多次

普通背包:i=0-n,j=w[i]-max  bag[j]=max( bag[j-w[i]]+v[i], bag[ j ] );

这样的话,每次循环所有的背包重量,看当前的物品是否可以加进去,那么此时一个背包就可能被使用多次了

多重背包:每个物品的数量有限

思路与完全背包中"将一种物品拆成多件物品"一样.将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为
1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种
物品分成系数分别为1,2,4,6的四件物品。

转化成01背包:

伪代码:

 procedure MultiplePack(cost,weight,amount)
    if cost*amount>=V
        CompletePack(cost,weight)
        return
    integer k=1
    while k<amount
        ZeroOnePack(k*cost,k*weight)
        amount=amount-k
        k=k*2
    ZeroOnePack(amount*cost,amount*weight)

View Code
1 #include <cstdio>
2 #include <cstring>
3
4  int f[100002],v[12],num[12],cost[12];
5  int total,n;
6  int max(int a,int b){ return a>b?a:b;}
7
8  void OneZeroPack(int cost,int value){
9 int i,j;
10 for(i = total;i >= cost; i--)
11 f[i] = max(f[i],f[i-cost]+value);
12
13 }
14  void completePack(int cost,int weight){
15 int i;
16 for(i = cost;i <= total; i++)
17 f[i] = max(f[i],f[i-cost]+weight);
18 }
19  void multiPack(int cost,int weight,int amount){
20 if(cost*amount >= total){
21 completePack(cost,weight);
22 return ;
23 }
24 int k = 1;
25 while(k < amount){
26 OneZeroPack(k*cost,k*weight);
27 amount -= k;
28 k*=2;
29 }
30 OneZeroPack(cost*amount,amount*weight);
31 }
32 int main(){
33 int i,j,k;
34 while(scanf("%d%d",&total,&n)!= EOF){
35 memset(f,0,sizeof(f));
36 for(i = 1;i <= n; i++){
37 scanf("%d%d",&num[i],&cost[i]);
38 v[i] = cost[i];
39 }
40 for(i = 1;i <= n; i++)
41 multiPack(cost[i],v[i],num[i]);
42 printf("%d\n",f[total]);
43 }
44 }
posted on 2011-05-07 14:32  void-man  阅读(316)  评论(0)    收藏  举报