poj1014 Dividing 动态规划 多重背包问题

第一次写多重背包的题,竟然1A,我真的十分地感动,哈哈。

题意:Marsha 和 Bill 收集了一些弹珠,弹珠因为大小和花纹不同,有不同的价值,他们想把弹珠分成价值相同的两份,这样对两个人才公平,

           问是否能够分成这样的两份。

#include <iostream>
using namespace std;

#define MAX(a, b) a>b?a:b

const int size = 7;
int dp[60010], amount[size];
int volume;
bool flag;

void ZeroOnePack(int c, int w)
{
     for(int v = volume; v >= c; v--)
	 { 
		dp[v] = MAX(dp[v-c]+w, dp[v]);
		if(dp[v] == volume)
		{
			flag = true;
			break;
		}
	 }
}

int main()
{
	int cas = 0, i, v, total;
	
	while(true)
	{
		cas++;
		memset(dp, -1, sizeof(dp));
		dp[0] = 0;
		total = 0;
		flag = false; //标记是否可分
		for(i = 1; i < size; i++)
		{
			scanf("%d", &amount[i]);
			total += i*amount[i];
		}
		if(total == 0) break;
	
		if(total%2 == 0) //一个剪枝,当价值为偶数时才有可能可分,否则,flag 为false。
		{
            volume = total/2;  //背包容量为弹珠总价值的一半,题意就是能否将这个背包装满。
            for(i = 1; i < size && !flag; i++)
			{
				if(amount[i] == 0) continue;
				if(i*amount[i] >= volume)
				{
					for(v = i; v <= volume; v++) //当价值为i的弹珠的总价值超过容量了,把问题转变成完全背包的问题。
					{
						dp[v] = MAX(dp[v-i]+i, dp[v]);
						if(dp[v] == volume) //当背包已经满了,直接跳出循环,停止DP
						{
							flag = true;
							break;
						}
					}
					continue;
				}
				/*当不为完全背包时,采用二进制优化(具体参见dd_engi的背包九讲),将多件相同的物品合成一件进行01背包,优化时间*/
				int k = 1;
				while(k < amount[i] && !flag) 
				{
					ZeroOnePack(k*i, k*i);
					if(flag) continue;
					amount[i] -= k;
					k *= 2;
				}
                ZeroOnePack(amount[i]*i, amount[i]*i); /*此处包含amount[i]为1和amount[i] > 1且amount[i]*i < volume的情况
													   (即不执行上面的while循环和执行while循环的两种情况)*/
			}
		}
		printf("Collection #%d:\n", cas);
		if(flag)
			printf("Can be divided.\n\n");
		else 
			printf("Can't be divided.\n\n");
	}
	return  0;
}


 

 

 

你可能感兴趣的:(poj1014 Dividing 动态规划 多重背包问题)