【POJ1014】Dividing 多重背包,二进制物品拆分转01背包

直接做01背包,即把物品数量累加,做20000物品的01背包指定TLE,不用我说了吧!

本文的优化是二进制优化,O(logn),至于完全背包记录已使用个数的O(n)算法本文不进行讲解,在博客的“背包”分类里。

二进制优化:

        大家知道一个十进制数可以转换成二进制,那么假设某种物品有1023种,即2^10-1,二进制为111111111,则可以视为每一位分别是一个由{1,2,4,8,16,32,64,128,256,512}个物品糅合成的大物品,二进制数每一位0表示不取,1表示取,这样我们就可以取遍所有的数,而若不是2的整数幂-1个物品,再补上缺的物品个数就好了,比如1025,就再补上个由2个物品组成的新物品。


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;


int n[7],sum;
bool f[121000];

int main()
{
//	freopen("test.in","r",stdin);
	int i,j,k,g;
	int flag;
	for(g=1;;g++)
	{
		flag=sum=0;
		for(i=1;i<=6;i++)
		{
			scanf("%d",&n[i]);
			if(n[i])flag=1,sum+=n[i]*i;
		}
		if(!flag)return 0;
		printf("Collection #%d:\n",g);
		if(sum&1)
		{
			printf("Can't be divided.\n\n");
			continue;
		}
		sum>>=1;
		memset(f,0,sizeof(f));
		f[0]=1;
		for(i=1;i<=6;i++)
		{
			/*二进制拆分*/
			int temp;
			for(j=0;(1<<j)<n[i];j++)
			{/*手模拟这个for循环(1<<j)的值,很好懂。*/
				n[i]-=(1<<j);
				temp=(1<<j)*i;
				for(k=sum-1;k>=0;k--)if(f[k]&&k+temp<=sum)f[k+temp]=1;
				if(f[sum])break;
			}
			if(n[i])
			{/*同题解说的“1025”那种情况*/
				temp=n[i]*i;
				for(k=sum-1;k>=0;k--)
				if(f[k]&&k+temp<=sum)
				f[k+temp]=1;
			}
			/**/
			if(f[sum])break;
		}
		if(f[sum])printf("Can be divided.\n\n");
		else printf("Can't be divided.\n\n");
	}
	return 0;
}


你可能感兴趣的:(背包,多重背包,二进制优化,POJ1014)