HDU - 1059 - Dividing
先上题目:
Dividing
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 14505 Accepted Submission(s): 4064
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.
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.
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.
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.
Source
题意:给你6种不同价值的硬币的数量,价值分别是1~6,问你能否可以将这些硬币分成两堆,每堆的价值一样。
多重背包,需要用二进制优化,之前一直以为用二进制优化的效果一般,所以打算学了用单调队列优化以后再码。前几天码了一题,就是用二进制优化的基础,然后对这种方法有了一定的了解,所以现在才做,但因为当时只是一知半解,所以做这题的时候WA了好几次。
分析如下:背包的容量是总价值,如果可以根据价值总和对分硬币,那说明总价值是偶数,同时dp[6][sum/2]==sum/2 意思就是前六种硬币放进一个容量值是总价值的一半的背包以后得到的最大价值应该就等于总价值的一半,如果这些条件成立,那就说明硬币可以根据价值对半分。
因为第一次用二进制优化,所以在这说一下自己的了解。
引用“背包九讲”里面的描述:我们考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略——取0..n[i]件——均能等价于取若干件代换以后的物品。
首先,对多重背包分析,对于某一种物品i,我们可以根据它的数量进行分类:
①amount[i]*cost[i]>=V 那就说明单用物品i来装的话可以装满整个背包,那这里可以将这种物品看成完全背包(相对于这个体积的背包,这种物品的数量可以看成是有无 限个)。
②amount[i]*cost[i]<V 那就说明单用这种物品无法装满整个背包,那么我们就可以将它看成是由多个不同种的物品,其中这里将它拆成多个不同种的物品的时候,新物品的价值和代价都是原来单个物品的倍数,而用二进制优化它,就是在拆分成多个物品的时候将倍数控制为2的不同次幂再加上一个数其中这个数等于(n-2^k+1),对于任何一个小于等于2^k-1的数,都可以用小于等于2^(k-1)的数的组合来表示(2的次幂转化为二进制就是100000······),同时因为n不一定就是(2^k-1),所以还需要加上一个(n-2^k+1),那么怎样表示(2^k-1)~n的任何数呢?经过分析,我们可以发现,①(n-2^k+1)<=(2^k-1);②(n-2^k+1)可以由小于等于2^(k-1)的2的次幂来组成;③我们可以把这个(n-2^k+1)作为一个新的n(我们可以叫它n'),那么我们又可以将n'像上面那样分解···最终我们可以同样用2的次幂来表示1~(n-2^k+1)的数(或者说(n-2^k+1)可以用1~2^(k-1)组合得来,然后再与1~2^(k-1)的数组合就可以得到2^k-1~n的其他数),总结起来就是我们可以用二的次幂的和来表示任何一个数,只是有的某个次幂需要不止一个。
用二进制优化的时候大致框架如下:
1 void MultiPack(int ost,int worth,int amount){ 2 if(amount*cost<V){ 3 4 for(int k=1;k<amount;k<<=1){ 5 ZeroOnePack(k*cost,k*worth); 6 amount-=k; 7 } 8 /*这里需要注意,因为amount可能还有剩余,所以还需要再跑一次01背包*/ 9 ZeroOnePack(amount*cost,amount*worth); 10 11 }else{ 12 13 CompletePack(cost,worth); 14 15 } 16 }
上代码:
1 #include <cstdio> 2 #include <cstring> 3 #define max(x,y) (x >= y ? x : y) 4 #define MAX 120002 5 using namespace std; 6 7 int a[7]; 8 int dp[MAX]; 9 int t; 10 11 void ZeroOnePack(int c,int w){ 12 for(int i=t;i>=c;i--){ 13 dp[i]=max(dp[i],dp[i-c]+w); 14 } 15 } 16 17 void CompletePack(int c,int w){ 18 for(int i=c;i<=t;i++){ 19 dp[i]=max(dp[i],dp[i-c]+w); 20 } 21 } 22 23 void MultiPack(int c,int w,int am){ 24 if(am*c<t){ 25 for(int l=1;l<am;){ 26 ZeroOnePack(l*c,l*w); 27 am-=l; 28 l<<=1; 29 } 30 ZeroOnePack(am*c,am*w); 31 }else{ 32 CompletePack(c,w); 33 } 34 } 35 36 int main() 37 { 38 //freopen("data.txt","r",stdin); 39 for(int cal=1;;cal++){ 40 int sum=0; 41 for(int i=1;i<=6;i++){ 42 scanf("%d",&a[i]); 43 sum+=a[i]*i; 44 } 45 if(sum==0) break; 46 printf("Collection #%d:\n",cal); 47 if(sum%2){ 48 printf("Can't be divided.\n\n"); 49 continue; 50 } 51 memset(dp,0,sizeof(dp)); 52 t=sum/2; 53 for(int i=1;i<=6;i++){ 54 MultiPack(i,i,a[i]); 55 } 56 if(dp[t]==t) printf("Can be divided.\n\n"); 57 else printf("Can't be divided.\n\n"); 58 } 59 return 0; 60 }