Hdu 1059 Dividing (DP_背包)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1059


题目大意:给定6堆硬币,每堆硬币的价值分别为1,2,3,4,5,6,现在给定每堆的硬币数量,问能否把这些硬币按价值平分?


解题思路:简单的多重背包,把每堆硬币按二进制处理成若干件物品,每堆的cost分别为2^0,2^1..2 ^(k-1), num - 2^k + 1,这样任意数量的硬币都可以用若干件物品组合得到,像整数的二进制表示,如13,可拆成1,2,4,6,那用这四个数可以表示13以内的任意数,如12,就是2+4+6.有了这个转换,假设物品变成tot个,问题就转化为用能否用tot个物品组合得到总价值为sum/2。状态转移方程:dp[j] |= dp[j-val[i]];(dp[j]表示是否可达,1可达,0不可达)

    本题有一个地方可以优化,那就是每个每个数量都对30取余,因为多余30的部分乘以价值的总和为30的倍数,可以被30整除,可以平分。


测试数据:

0 2 2 4 0 0


1 3 5 7 9 13


1 3 5 7 9 11


1 0 1 2 0 0

代码:

#include <stdio.h>
#include <string.h>
#define MAX 210000


int cost[MAX],val[MAX],v;
int n,dp[MAX],sum,num[10];


int Divide(int sum) {

	int i,j,k,tot = 0;
	
	
	for (i = 1; i <= 6; ++i) {
		//多重背包转化为01背包
		if (num[i] == 0) continue;
		for (k = 0; (1<<k) <= num[i]; ++k) {

			num[i] -= (1<<k);
			cost[++tot] = (1<<k);
			val[tot] = (1<<k) * i;
		}

		cost[++tot] = num[i];
		val[tot] = num[i] * i;
	}


	memset(dp,0,sizeof(dp));
	dp[0] = 1;
	for (i = 1; i <= tot; ++i)
		for (j = sum; j >= val[i]; --j)
			dp[j] |= dp[j-val[i]];
	return dp[sum];
}


int main()
{
	int i,j,k,t;
	int flag,cas = 0;


	while (scanf("%d",&num[1])) {

		for (i = 2; i <= 6; ++i)
			scanf("%d",&num[i]),num[i] %= 30;
		sum = v = 0;
		for (i = 1; i <= 6; ++i)
			sum += num[i] * i,v += num[i];
		if (sum == 0) break;

		
		flag = 0;
		if (sum & 1) flag = 0;
		else  flag = Divide(sum/2);
		printf("Collection #%d:\n",++cas);
		if (flag) printf("Can be divided.\n\n");
		else printf("Can't be divided.\n\n");
	}
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

你可能感兴趣的:(优化,测试)