1 0 1 2 0 0 1 0 0 0 1 1 0 0 0 0 0 0
Collection #1: Can't be divided. Collection #2: Can be divided.
题意:一共有6种物品,价值分别是1,2,3,4,5,6,给出每种物品的数目,判断师傅能够平分
思路:典型的多重背包,防止超时要进行一些必要的剪枝和二进制优化
多重背包二进制拆分实现:
跟完全背包一样的道理,利用二进制的思想将n[i]件物品i拆分成若干件物品,目的是在0-n[i]中的任何数字都能用这若干件物品代换,另外,超过n[i]件的策略是不允许的。
方法是将物品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的四件物品。分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示。
代码如下:
#include
#include
#include
using namespace std;
int dp[100000];
int main()
{
int a[11],sum,v,i,j,k,cnt,cas = 1;
while(~scanf("%d",&a[1]))
{
sum = a[1];
for(i = 2;i<=6;i++)
{
scanf("%d",&a[i]);
sum+=i*a[i];
}
if(!sum)
break;
printf("Collection #%d:\n",cas++);
if(sum%2)//总和为奇数,必定不能平分
{
printf("Can't be divided.\n\n");
continue;
}
v = sum/2;
memset(dp,0,sizeof(dp));
dp[0] = 1;
for(i = 1;i<=6;i++)
{
if(!a[i])
continue;
for(j = 1;j<=a[i];j*=2)//二进制优化
{
cnt = j*i;
for(k = v;k>=cnt;k--)
{
if(dp[k-cnt])//必须前面的能够放入背包,现在的才能放入背包
dp[k] = 1;
}
a[i]-=j;
}
cnt = a[i]*i;//剩下的
if(cnt)
{
for(k = v;k>=cnt;k--)
{
if(dp[k-cnt])
dp[k] = 1;
}
}
}
if(dp[v])
printf("Can be divided.\n\n");
else
printf("Can't be divided.\n\n");
}
return 0;
}