POJ 1014 -- Dividing

题意:

有6种物品,价值分别为1,2,3,4,5,6。数量分别为n1,n2,n3,n4,n5,n6(由输入决定)。请你判断能否把这些物品分成两份,使两份的价值相等。

输入为n1~n6,输出能否均分,当输入0 0 0 0 0 0 结束程序。

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.

思路

这是一道多重背包的问题,最近在看《背包九讲》,可以直接套用里面的模型,也可以自己把它转化为01背包问题。

另外YoU http://blog.csdn.net/lyy289065406/article/details/6661449,大神给出了一个DFS解法,虽然能够在POJ上0MS AC,但是0 0 3 0 5 1这个测试数据却过不了。我自己写的DFS解法超时,应该有不超时的DFS,要好好想想!

源代码:

#include
#include
#include

using namespace std;

const int MAX = 60000;
const int NINF = -(1<<30);
int n[7];
int v;
int dp[MAX+1];
int flag;	//完成的标志

int max(int a,int b)
{
	return a>b?a:b;
}

//01背包
void ZeroOnePack(int cost,int weight)
{
	for(int i = v;i >= cost;i--)
	{
		dp[i] = max(dp[i],dp[i-cost]+weight);
		if(dp[v] == v)	//满足结束条件
		{
			flag = 1;
			return;
		}
	}
}

//多重背包
//为什么我只用 01背包 比 01背包+多重背包混合 更快?
void MultiplePack(int cost,int weight,int amount)
{
	int k = 1;
	while(k < amount)
	{
		ZeroOnePack(k*cost,k*weight);
		if(flag)
			return;
		amount -= k;
		k *= 2;
	}
	ZeroOnePack(amount*cost,amount*weight);
}

int main()
{
	int test = 1;
	int end;
	int i;
	while(1)
	{
		end = 1;
		v = 0;
		flag = 0;
		for(i = 1;i <= 6;i++)
		{
			cin>>n[i];
			if(n[i]) end = 0;
			v += i*n[i];
		}
		if(end) break;
		
		printf("Collection #%d:\n",test++);

		if(v%2) //和是奇数
		{
			printf("Can't be divided.\n\n");	//一定要注意还要空出一行
			continue;
		}

		v /= 2;	//每一份的值

		memset(dp,0,sizeof(dp));

		for(i = 1;i <= 6;i++)
		{
			MultiplePack(i,i,n[i]);
			if(flag)
				break;
		}
		if(flag)
			printf("Can be divided.\n\n");
		else printf("Can't be divided.\n\n");
	}
	return 0;
}


你可能感兴趣的:(算法)