hdu1059&poj1014 Dividing (dp,多重背包的二分优化)

Problem Description
Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value. 
Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.
 
 Input
Each line in the input describes one collection of marbles to be divided. The lines consist of six non-negative integers n1, n2, ..., n6, where ni is the number of marbles of value i. So, the example from above would be described by the input-line ``1 0 1 2 0 0''. The maximum total number of marbles will be 20000. 
The last line of the input file will be ``0 0 0 0 0 0''; do not process this line.
 
 Output
For each colletcion, output ``Collection #k:'', where k is the number of the test case, and then either ``Can be divided.'' or ``Can't be divided.''. 
Output a blank line after each test case.
 
 Sample Input
1 0 1 2 0 0
1 0 0 0 1 1
0 0 0 0 0 0
 Sample Output
Collection #1: Can't be divided.
Collection #2: Can be divided.
 
  题目的意思大概就是有6石头价值分别为1~6,现在给你这6种石头的个数,问你能否把它们分为价值相等的两堆。
  第一遍读完题觉得这是一个多重背包,准备模板直接过,但是看了看数据的范围,石头的个数能达到20000!铁铁的TLE。所以网上有学了一招----多重背包的二分优化。
  我们先来回顾一下,多重背包的本质还是01背包,01背包的本质是什么呢?我个人感觉就是把每种物品是否装进背包的情况遍历一遍。
  现在我们来看这样的问题,给你20个价值为1的物品,如果按照01背包的思路就要把这20个物品挨个判断是否加入背包。如果我们按照二进制的想法呢?
将这20个物品合并成1(2^0),2(2^1),4(2^2),8(2^3),5(20-1-2-4-8=5)这5个物品,只对这四个物品进行是否加入背包的判断是不是就大大减少了复杂度?现在问题转化为这两种背包是否等价?也就是用1,2,4,8,5这5个数能否组合出1~20里所有的数?  答案是显然可以的!前面2^n部分可利用二进制来表示1~15,从11到15上再加上那个5,就能表示16~20。如此,我们把个数较多的石头按2^n进行合并,最后剩下的单独一个(例如上面的5),这就是多重背包的二进制优化。
代码如下 :
 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 int num[7],sum,v,cnt,i,j;
 5 bool dp[240000];//dp[i]表示容量为i的背包能否装满
 6 int main()
 7 {
 8     //freopen("de.txt","r",stdin);
 9     int casenum=1;
10     while (1)
11     {
12         memset(dp,0,sizeof dp);
13         memset(num,0,sizeof num);
14         sum=0;
15         for (i=1;i<=6;++i)
16         {
17             scanf("%d",&num[i]);
18             sum+=i*num[i];
19         }
20         if (sum==0)
21         break;
22         printf("Collection #%d:\n",casenum++);
23         if (sum%2)//如果所有石头总价值和为奇数肯定不能均分
24         {
25             printf("Can't be divided.\n\n");
26             continue;
27         }
28         else
29         {
30             v=sum/2;//用总价值的一半来作为背包的容量
31             dp[0]=1;
32             for (i=1;i<=6;++i)
33             {
34                 if (num[i]==0)
35                 continue;
36                 for (j=1;j<=num[i];j*=2)//j来作为2^n的单位
37                 {
38                     cnt=i*j;
39                     for (int k=v;k>=cnt;--k)
40                     {
41                         if (dp[k-cnt])//只有k-cnt的背包被装满,k的背包才能被装满
42                         dp[k]=1;
43                     }
44                     num[i]-=j;//分完2^n剩下的个数
45                 }
46                 cnt=num[i]*i;
47                 if (cnt)
48                 {
49                     for (int k=v;k>=cnt;--k)
50                     {
51                         if (dp[k-cnt])
52                         dp[k]=1;
53                     }
54                 }
55             }
56             if (dp[v])//容量为v的背包能否被装满
57             printf("Can be divided.\n\n");
58             else
59             printf("Can't be divided.\n\n");
60         }
61     }
62     return 0;
63 }

 

 
posted @ 2016-08-21 13:50  抓不住Jerry的Tom  阅读(532)  评论(0编辑  收藏  举报