我的理解:首先对于一件物品来说,如果这个物品的容量数目已经大于背包的总容量了,那么这与完全背包问题没有区别了。因为完全背包问题就是可以任意的往背包里面放物品,而这时这个物品的容量数目已经大于背包总容量了,所以,也就相当于在背包总容量的范围内,这个物品是可以支持 任意地往背包里面放物品。
如果这个物品的容量*数目小于背包的总容量,那么正常情况下,需要一件一件往里面放找最大值的。但是它的方法是:将第i种物品分成若干件01背包中的物品,其中每件物品有一个系数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。令这些系数分别为1,2, 2的2次方,2的三次方,。。。2的k-1次方,最后是Mi - 2的k次方 + 1,且k是满足Mi - 2的k次方 + 1 的最大整数。例如,如果Mi为13,则相应的k=3,这种最多取13件物品的应被分成系数分别为1 2 4 6 的四件物品。
我觉得这里设计的很nice在于,正常情况下,举例来说这个物品有13件,我们需要一件一件放,放1件,2件,3件,。。。13件都放入,然后不断找最大值。但是上面的方法利用二进制,4个数,就做了13个数的事情。
发现 1 2 4 6 这四个数相互之间的和 就已经可以表示1到13的所有数,原因就在于当我放入1件物品,再放入2件物品找最大值的时候,实际上相当于我一下子放入了该物品的3件,所以实际上3是不需要我们再去实验的了。
//w是物品体积,n是物品种类,num[]是物品数量,price[]是物品价值,v[]是物品体积
这是最近写的
#include <iostream>
#include <string.h>
using namespace std;
int v[1005],price[1005],num[1005];
long long dp[100005];
void Zero_Pack(int value,int vv,int w)
{
for(int i=w;i>=vv;i--)
dp[i]=max(dp[i],dp[i-vv]+value);
}
void Complete_Pack(int value,int vv,int w)
{
for(int i=vv;i<=w;i++)
dp[i]=max(dp[i],dp[i-vv]+value);
}
long long Pack(int v[],int price[],int num[],int n,int w)
{
for(int i=1;i<=n;i++)
{
if(num[i]*v[i]>w)
{
Complete_Pack(price[i],v[i],w);
}
else
{
int k=1;
while(k<num[i])
{
Zero_Pack(k*price[i],k*v[i],w);
num[i]-=k;
k=k<<1;
}
Zero_Pack(num[i]*price[i],num[i]*v[i],w);
}
}
return dp[w];
}
void init()
{
memset(dp,0,sizeof(dp));
memset(price,0,sizeof(price));
memset(num,0,sizeof(num));
memset(v,0,sizeof(v));
}
int main()
{
ios::sync_with_stdio(false);
int w,n;
while(cin>>w>>n)
{
init();
for(int i=1;i<=n;i++)
{
cin>>num[i]>>price[i];
v[i]=price[i];//这里物品价值相当于物品体积
}
long long ans=Pack(v,price,num,n,w);
cout<<ans<<endl;
}
return 0;
}
这是之前写的