原题: http://poj.org/problem?id=1014
一开始知道是多重背包,然后自己弄成01背包就交了,结果超时,所以百度后才知道需要用二进制优化
举个例子,假如说物品i有10个,单价为 2,而最后的最优结果取i是7个。传统的做法就是直接化成01背包,也就是把10个物品i看成10个一模一样的物品(w=1,v=2)然后送去dp
改良的方法就是压缩这个数量10: 我们可以把数量10二进制分解出来得到1 2 4 3(最后这个3是为了可以凑到刚好等于10)
则我们可以把10个物品看成四个物品 物① w=1,v=2; ② w=2 v=4 ③ w=4 v=8 ④ w=3 v=6 ,然后把这4个物品拿去dp,可以看出这样子数量就被我们压缩成4个了!
为什么可以把10二进制分解而结果正确? 因为我们最后选取的n=7个也是可以分成二进制,而这些个二进制必然是(1,2,4,3)中的某些个。
PS:小小的再优化:在多重背包中 如果某些物品的数量num[i]>sum / v[i],还可以把这个物品当成完全背包
启发来自: http://blog.csdn.net/qq_33171970/article/details/50582671
#include<stdio.h>
int main()
{
const int size = 7;
int num[size];
int t = 0;
while(true)
{
t++;
int sum=0;
for(int i=1;i<=6;i++)
{
scanf("%d",&num[i]);
sum=sum+num[i]*i;
}
if(sum==0)break;
if(sum%2==1)
{
printf("Collection #%d:\n",t);
printf("Can't be divided.\n\n");
}else{
sum=sum/2;//看能不能凑到这个sum值
int dp[60010]={0};
for(int i=1;i<=6;i++)//一共只有六种
{
if(num[i]==0)continue;
if(num[i]==1)//01背包
{
for(int j=sum;j>=i;j--)
{
if(dp[j]<dp[j-i]+i)
{
dp[j]=dp[j-i]+i;
}
}
}else if(sum/i<=num[i])//完全背包
{
for(int j=i;j<=sum;j++)
{
if(dp[j]<dp[j-i]+i)
{
dp[j]=dp[j-i]+i;
}
}
}else{//多重背包
//分割
int k = 1;//单个物品的重量
int tmp = num[i];
while(k<=tmp)
{
int val = k*i;//单个物品的价值
for(int j=sum;j>=val;j--)
{
if(dp[j]<dp[j-val]+val)
{
dp[j]=dp[j-val]+val;
}
}
k=k*2;
tmp=tmp-k;
}
if(tmp>0)
{
int val = tmp*i;//单个物品的价值
for(int j=sum;j>=val;j--)
{
if(dp[j]<dp[j-val]+val)
{
dp[j]=dp[j-val]+val;
}
}
}
}
}
printf("Collection #%d:\n",t);
if(dp[sum]==sum)
{
printf("Can be divided.\n\n");
}else{
printf("Can't be divided.\n\n");
}
}
}
return 0;
}