本题作为多重背包二进制优化模板
思路:一个数是能用二进制表达出来的,多重背包有数量限制且按照常规三个for容易超时,因为第三个循环对数量的处理是逐个进行的,比如有10个,那就要执行10次.
因此通常我们把数量拆分,比如说10可以分装成(1+2+4)+3,那4是怎么来的,这个4是二进制的结束点,10-(1+2)>2²,所以继续进行,10-(1+2+4)<2³ ,即停止,此时余数为3,。二进制有个结论就是(2⁰+2¹+2²+…2ⁿ )的和∑最接近且小于X时,0~∑之间所有数都能被这个2的次幂组合出来.
比如10里面的1可以由1得到,2由2得到,3由1+2得到,4由4得到,5由1+4得到,只需要加上X-∑(余数),那么0~10全部数字都可以表示出来,这样我们相当于把几个物品合成一个,对应的价值和质量也就翻了几倍,就像炼铁一样。
这一题可以看成给你一个数y,
你要用小于y的数x1,x2,x3,…xn加起来等于y,y可以看成背包问题中的容量同时也是价值的上限,xn可看成体积与价值都等于xn的物品,然后判断最大值是否为价值上限y,是即填满了背包。
这里时间复杂度变成了O(n*logm);
比如10≈2³,即可拆分成1,2,4三个产品,执行了三次循环
#include<stdio.h>
#include<string.h>
#define max(a,b) a>b?a:b
int value[200005],dp[200005];
int main()
{
int a[7],r=0;
scanf("%d %d %d %d %d %d",&a[1],&a[2],&a[3],&a[4],&a[5],&a[6]);
while((a[1]+a[2]+a[3]+a[4]+a[5]+a[6])!=0)
{
int n=0,m=0,j,t=0,k,i,u,y,x,e,b,c,d;
r++;x=(a[1]+a[2]*2+a[3]*3+a[4]*4+a[5]*5+a[6]*6);
if(x%2!=0)
{
printf("Collection #%d:\nCan't be divided.\n\n",r);
}
else
{
x=x/2;
for(i=1;i<=6;i++)
{
c=1;e=a[i];
while(c<e)
{
t++;
value[t]=c*i;
e=e-c;
c=c*2;
}
t++;
value[t]=e*i;
}
value[0]=0;
for(i=0;i<=t;i++)
{
for(j=x;j>=value[i];j--)
{
if(i==0&&j==0)
{
dp[j]=0;
}
else if(i==0&&j!=0)
{
dp[j]=-999999;
}
else
{
dp[j]=max(dp[j],dp[j-value[i]]+value[i]);
}
}
}
if(dp[x]==x)
{
printf("Collection #%d:\nCan be divided.\n",r);
}
else
{
printf("Collection #%d:\nCan't be divided.\n",r);
}
printf("\n");
}
scanf("%d %d %d %d %d %d",&a[1],&a[2],&a[3],&a[4],&a[5],&a[6]);
}
return 0;
}