(POJ-1276)Cash Machine(二进制优化+多重背包)

题目链接:1276 -- Cash Machine

题意:先给你一个背包容量,然后给你n种物品每种物品的体积和数量,问最多能装的物品的体积是多少?

多重背包经过二进制优化就相当于01背包,什么是二进制优化呢?就是说一种物品有m件,我们在多重背包问题求解过程中会依次遍历取多少件,这样复杂度有点高,而二进制优化就是说把2^s件物品当成一件物品来取,这样原来需要取2^s次现在只需要取一次,复杂度会大大降低,那这样做的正确性应该如何证明呢?

我们假设最优答案中包含这种物品k件,然后我们把m分成若干个连续的2的次幂的和,也就是m=1+2+4+8+……+2^s+p(0<=p<=2^(s+1)),共s+2个数,其中p不一定是2的幂次,那么k一定可以由这s+2个数中的若干来表示,由于这些数的和是大于等于k的且二的幂次是连续的,所以用这些数可以表示出来任意的小于等于m的数,现在对这个地方给出证明:

(1)k<=2^(s+1)-1,这个时候写出k的二进制表示,第几位是1就取第几个物品,比如k的二进制表达式是10011(s>=5),这个时候由于二进制表示中第1,2,5位是1,所以我们就可以取第1,2,5件物品,这样就可以表示出k了

(2)2^(s+1)<=k<=p+2^(s+1)-1=m,则有k-p<=2^(s+1)-1,这就意味着我们可以先取出来p件物品,再写出剩余需取物品的二进制表示,然后同上,k-p二进制表示中哪位是1就取第几件物品

有了这一点之后这道题目就变成了一道普通的01背包问题了,把m个物品分解成若干连续二的幂次的方法见代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=1e5+10;
int f[N],v[N];
int main()
{
	int V,n;
	while(scanf("%d",&V)!=EOF)
	{
		scanf("%d",&n);
		memset(f,0,sizeof f);
		int cnt=0;
		for(int i=1;i<=n;i++)
		{
			int s,t;
			scanf("%d%d",&s,&t);
			for(int j=1;j<=s;j<<=1)//将物品数目拆分成若干个2进制(最后一个物品可能不是二进制数,但不影响 ) 
			{
				v[++cnt]=j*t;
				s-=j;
			}
			if(t)
				v[++cnt]=t*s;
		}
		for(int i=1;i<=cnt;i++)
			for(int j=V;j>=v[i];j--)
				f[j]=max(f[j],f[j-v[i]]+v[i]);
		printf("%d\n",f[V]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/AC__dream/article/details/123912843