1. 程式人生 > >【POJ1014】Dividing 多重揹包,二進位制物品拆分轉01揹包

【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;
}