hdu1059

题意,现在有价值为1、2、3、4、5、6的石头若干块,块数已知,问能否将这些石头分成两堆,且两堆价值相等。

很显然,愚蠢的我一开始并想不到什么多重背包二进制优化```因为我连听都没有听过```不得不吐槽自己的知识面太窄```于是,我用了母函数写这题,母函数的做法并没有问题,但是由于这道题的数据很大,母函数轻轻松松就超时了,于是,我又很努力地在母函数循环的优化上面想出路,改改改,各种改之后依旧TLE,01背包的做法显然也是会超时的,DISCUSS里的母函数做法优化方式都是模上一个大质数,但很明显,只是因为数据较弱才会过的,因为没有人能保证被这个数模掉的那些部分是否可以平分,模掉之后剩下的数也很有可能就因此不能平分了所以这种优化肯定是不可取的。

问过学长粗看了题解之后才知道原来还有一种叫多重背包的东西,学长推荐我看背包九讲,我看了之后才会做这个题目```

多重背包其实本身也是01背包,但由于有多个价值相同的物品,所以有更加优化的解法就是用二进制优化,将相同价值的物品分为 20, 21 , 22 ,```个,最后再有一部分不能继续向二进制高阶划分的,将这些被划分的物品分别合成一个物品,那么就得到了价值为 1 倍的、2倍的、4倍的```物品,然后再按照01背包的做法,通过二进制数的加和可以实现物品的所有放入情况。

 

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define max(a,b) a>b?a:b
 4 int Va[100],We[100],n[7],dp[200000];
 5 
 6 int main(){
 7     int c=0;
 8     while(scanf("%d%d%d%d%d%d",&n[1],&n[2],&n[3],&n[4],&n[5],&n[6])!=EOF&&(n[1]!=0||n[2]!=0||n[3]!=0||n[4]!=0||n[5]!=0||n[6]!=0)){
 9     //    if(c)printf("\n");
10         printf("Collection #%d:\n",++c);
11         int i,j,sum=0,mid;
12         for(i=1;i<=6;i++)sum+=i*n[i];
13         if(sum%2){
14             printf("Can't be divided.\n\n");
15             continue;
16         }
17         else mid=sum/2;
18         memset(dp,-1,sizeof(dp));
19         int count=0,temp;
20         for(i=1;i<=6;i++){
21             temp=1;
22             while(n[i]>=temp){
23                 Va[++count]=i*temp;
24                 We[count]=temp;
25                 n[i]-=temp;
26                 temp*=2;
27             }
28             if(n[i]>0){
29                 Va[++count]=i*n[i];
30                 We[count]=n[i];
31             }
32         }
33         dp[0]=0;
34         for(i=1;i<=count;i++){
35             for(j=mid;j>=Va[i];j--){
36                 if(dp[j-Va[i]]>=0){
37                     dp[j]=max(dp[j],dp[j-Va[i]]+We[i]);
38                 }
39             }
40         }
41         if(dp[mid]>0)printf("Can be divided.\n\n");
42         else printf("Can't be divided.\n\n");
43     }
44     return 0;    
45 }
View Code