POJ1014:Dividing

点击打开题目链接


Dividing

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 54312 Accepted: 13895

Description

Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value. Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.

Input

Each line in the input file describes one collection of marbles to be divided. The lines contain six non-negative integers n1 , . . . , n6 , where ni is the number of marbles of value i. So, the example from above would be described by the input-line "1 0 1 2 0 0". The maximum total number of marbles will be 20000. 
The last line of the input file will be "0 0 0 0 0 0"; do not process this line.

Output

For each collection, output "Collection #k:", where k is the number of the test case, and then either "Can be divided." or "Can't be divided.". 
Output a blank line after each test case.

Sample Input

1 0 1 2 0 0 
1 0 0 0 1 1 
0 0 0 0 0 0 

Sample Output

Collection #1:
Can't be divided.

Collection #2:
Can be divided.

Source


=====================================题目大意=====================================


有价值分别为1,2,3,4,5,6的六种弹珠每种若干个,求解是否能把它们划分为价值相等的两部分。


=====================================算法分析=====================================


多重背包,略加修改完全背包的解法或者直接转换为01背包,O(N*V*∑Sum[i])的算法在本题会超时。

然后就去学了二进制拆分的方法,感觉背包九讲的有点复杂,还是记下自己的理解好了。

把多重背包直接转换为01背包做的时候,会对同一类物品中的每一件物品都做出01决策,显然这是会产生大量等价状态的。

于是就有了这样一种思想:对于同一类物品,将它们进行组合,几件物品组合成为一件,用“对组合物品的01决策”来代替“对

个物品的01决策”。

物品件数减少了,01背包DP的效率自然就提高了。

不过这里的前提不能忽视,那就是“对组合物品的01决策”必须能够表示出所有“对该类物品的决策”。

比如说有N件X类物品,那么“对X物品的决策”就有N+1种(选0-N件),而显然“对单个物品进行01决策”的方法是可以表示出它们

的任意一种的。

现在将它们组合成A1X,A2X,A3X......AmX(AiX表示用Ai件X物品组合而成的组合物品)。

那么就要求,“对这m件组合物品的01决策”能够表示出“对X物品的N+1种决策”中的任意一种。

其实这也就是要求,[0,N]中的任意整数都可以通过选取A1-Am中的数字相加得到(不选任何数字的时候得到0)。

再考虑到∑Ai(1<=i<=M)=N,就不难想到二进制拆分了:

“(2^K)-1可以拆分为“2^0,2^1,2^2......2^(K-1)”,而通过选取这些数字相加便可得到[0,(2^K)-1]中的任意整数。

那么如果N是2的幂减1,问题就解决了,比如N=15=2^4-1,组合方案就是A1=2^0=1,A2=2^1=2,A3=2^2=4,A4=2^3=8。

也就是说,把1,2,4,8件X物品组合成A,B,C,D四个,这样15件物品就成了4件组合物品。

而“对这4件组合物品的01决策”就可以表示出“对X类物品的16种决策”中的任意一种,比如选取3件X物品就可以用选取A和C来表示。

好了,那么当N不是2的幂减1呢?仍然上述方法进行组合,2^0件组合为1件,2^1件组合为1件,2^2件组合为1件。。。。。。

因为N不是2的幂减1,所以最后就是这种情况:把2^(K-1)件组合为1件之后,发现剩下还有X物品,但已不足2^K件,无法按照之前的

法继续组合。

这时候怎么办呢?其实很简单,把剩下的所有X物品组合成1件就行了,下面说明一下这样的组合方案仍然满足要求。

情况与上述相同,不过需要假设剩余有R件X物品,根据上述可知R<2^K。

这样最终得到的组合物品系数Ai为:2^0,2^1,2^2......2^(K-1),R。

根据之前的论证可知“2^0,2^1,2^2......2^(K-1)”可以组合出区间[0,(2^K)-1]中任意整数,那么在这个基础上加上R,就可以得

区间[R,R+(2^K)-1]中任意整数。

因为R<2^K,R<=(2^K)-1,所以[0,(2^K)-1]∪[R,R+(2^K)-1]=[0,R+(2^K)-1]=[0,N]。

也就是说通过选取“2^0,2^1,2^2......2^(K-1),R”中的数字相加可以得到区间[0,N]中的任意整数,因而这种拆分方案仍然满足要求。

最后还有一点,当Cost[X]*Sum[X]>=PackVol时,物品X相对于背包就是无限的,此时对物品X就可以用完全背包的方法来处理。


=======================================代码=======================================



#include<stdio.h>
#include<string.h>

int Marble[10],F[120005];

int inline MAX(int A,int B)
{
	return A>B?A:B;
}

void inline ZeroOnePack(int ResVal,int ResVol,int BpCap)
{
	for(int i=BpCap;i>=ResVol;--i)
	{
		F[i]=MAX(F[i],F[i-ResVol]+ResVal);
	}
}

void inline CompletePack(int ResVal,int ResVol,int BpCap)
{
	for(int i=ResVol;i<=BpCap;++i)
	{
		F[i]=MAX(F[i],F[i-ResVol]+ResVal);
	}
}

void MultiplePack(int ResVal,int ResVol,int ResNum,int BpCap)
{
	if(ResVol*ResNum>=BpCap) { CompletePack(ResVal,ResVol,BpCap); }
	for(int i=0;(1<<i)<=ResNum;++i)
	{
		ZeroOnePack((ResVal<<i),(ResVol<<i),BpCap);
		ResNum-=(1<<i);
	}
	if(ResNum) { ZeroOnePack(ResVal*ResNum,ResVol*ResNum,BpCap); }
}

int ReaData()
{
	int sum=0;;
	for(int i=1;i<=6;++i)
	{
		scanf("%d",&Marble[i]);
		sum+=i*(Marble[i]);
	}
	return sum;
}

int main()
{
	int sum,cas=1;;
	while(sum=ReaData())
	{	
		bool flag=false;
		if(!(sum&1))
		{
			memset(F,0,sizeof(F));
			int half=(sum>>1);
			for(int i=1;i<=6;++i)
			{
				if(Marble[i]) { MultiplePack(i,i,Marble[i],half); }
			}
			flag=(F[half]==half);
		}       
		printf("Collection #%d:\nCan%sbe divided.\n\n",cas++,(flag)?(" "):("'t "));
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值