多重背包POJ1276不要求恰好装满 poj1014多重背包恰好装满

二进制压缩,看背包九讲啦,写的很详细了,不赘述````

先贴不要求恰好装满的情形

POJ 1276

二进制压缩:

 1 #include <cstdio>
 2 #include <cstring>
 3 int f[100005];//容量为i时最多能装多少
 4 int p[12000];//物品拆分
 5 int main()
 6 {
 7     int n,cash;
 8     while(~scanf("%d%d",&cash,&n))
 9     {
10         int vi,num;//物品价值,数量
11         int cur = 0;
12         memset(f,0,sizeof(f));
13         for(int i=0; i<n; i++)
14         {
15             scanf("%d%d",&num,&vi);
16             int k = 1;
17             //把物品拆分成num = 1 + 2 + 4 + 8 + 16 + 32 + ``` + 2^k + l(剩余的值)
18             //假设某物品有10件,拆成7=1+2+4+3。。这样,10件这样的物品变成了4件物品,用这四种物品可组和出
19             //0-10的每一种情形,原理在于二进制,每位取0或者1,但是能表示出很多数
20             //多重背包转化成了0-1背包
21             while(num-k > 0)
22             {
23                 p[cur]= vi*k;
24                 num -= k;
25                 k *= 2;
26                 cur++;
27             }
28             p[cur] = vi*num;
29             cur++;
30         }
31         for(int i=0; i< cur; i++)
32         {
33             for(int j = cash; j >= p[i]; j--)
34             {
35                 if(f[j] < f[j-p[i]]+p[i])
36                     f[j] = f[j-p[i]]+p[i];
37             }
38         }
39         printf("%d\n",f[cash]);
40     }
41     return 0;
42 }
View Code

另一种算法,感觉挺好的。。。

 1 #include <cstdio>
 2 #include <cstring>
 3 //#define debug
 4 int count[100005];//被使用的次数
 5 bool vis[100005]; //是否恰好能被零散货币表示
 6 int main()
 7 {
 8     int n,i,j,cash;
 9 #ifdef debug
10     freopen("in.cpp","r",stdin);
11 #endif
12     while(~scanf("%d",&cash))
13     {
14         scanf("%d",&n);
15         memset(vis,0,sizeof(vis));//vis用来表示是否恰好被装满
16         vis[0] = 1;
17         for(i=0; i<n; ++i)
18         {
19             int v,num;
20             scanf("%d%d",&num,&v);
21             memset(count,0,sizeof(count));//用来记录该物品被用过几次,每次都得重新赋值,麻烦的
22             for(j=v; j<=cash; ++j)
23             {
24                 int t = j-v;
25                 if(!vis[j] && vis[t] && count[t] < num)//用过的次数>=num。就不行了
26                 {
27                     vis[j] = 1;
28                     count[j] = count[t] + 1;
29                 }
30             }
31         }
32         for(i=cash; i>=0; --i)
33         {
34             if(vis[i])
35             {
36                 printf("%d\n",i);
37                 break;
38             }
39         }
40     }
41     return 0;
42 }
View Code

该算法复杂度为O(N*M),貌似和单调队列优化的复杂度差不多

再贴恰好要装满的情形

POJ 1014

View Code
 1 #include <cstdio>
 2 int f[500000];
 3 int p[2000];
 4 int main()
 5 {
 6     int ser = 0;
 7     while(1)
 8     {
 9         int n[7];
10         int sum =0;
11         int cur =0;
12         for(int i=1; i<= 6; i++)
13         {
14             scanf("%d",&n[i]);
15             sum += i*n[i];
16             int k = 1;
17             while(n[i] - k > 0)
18             {
19                 p[cur] = i*k;
20                 cur++;
21                 n[i] -= k;
22                 k *= 2;
23             }
24             p[cur] = n[i]*i;
25             cur++;
26         }
27         if(sum == 0) break;
28         if(sum%2 == 1)
29         {
30             printf("Collection #%d:\n",++ser);
31             printf("Can't be divided.\n\n");
32             continue;
33         }
34         sum = sum/2;
35         for(int i=1 ; i <= sum; i++)
36             f[i] = -1000;
37         f[0] = 0;
38         for(int i=0; i < cur && f[sum] < 0; i++)
39         {
40             for(int j= sum; j >0; j--)
41             {
42                 if(j - p[i] < 0) continue;
43                 if(f[j-p[i]] >= 0 && f[j-p[i]]+p[i] > f[j])
44                 {
45                     f[j] = f[j-p[i]]+p[i];
46                     if(j == sum )  break;
47                 }
48             }
49         }
50         printf("Collection #%d:\n",++ser);
51         if(f[sum] == sum)
52             printf("Can be divided.\n\n");
53         else
54             printf("Can't be divided.\n\n");
55     }
56     return 0;
57 }

 

转载于:https://www.cnblogs.com/allh123/archive/2013/03/26/2983236.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值