题目链接: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"); } }