题目的意思:
有一堆大理石,按其大小分为1-6种价值,两个人想分得的价值相同的大理石,已知每种价值的大理石个数,问是否能让两个人分得价值相同的大理石?
题目输入:
1 0 1 2 0 0
1 0 0 0 1 1
0 0 0 0 0 0
题目输出:
Collection #1:
Can't be divided.
Collection #2:
Can be divided.
解题思路:
很明显这是一个多重背包问题,可以将问题转化为0/1背包问题解决。将每种价值v的大理石数x,按二进制分解成价值为1*v,2*v,4*v…,2^k-1*v,(x-2^k+1)*v的大理石,其它价值的大理石也这样分解,这样就转化成为0/1背包问题了(由于1,2,4…,2^k-1,x-2^k+1可以组成0~x中的任意数值)。后面的问题就简单了,只要能找到一种取分解后大理石的取法,使取得后的大理石的总价值等于全部大理石总价值的一半就说明可以平均分配,否则就不能。
代码:
#include <stdio.h> #include <string.h> int ans[120001]; int half; int data[7]; int tpe[20]; int main(void) { int count = 1; int i,j,k; int cur_max; tpe[0] = 1; for (i = 1; i < 20; i ++) tpe[i] = 2 * tpe[i - 1]; while (1) { scanf("%d%d%d%d%d%d", data + 1, data + 2, data + 3, data + 4, data + 5, data + 6); if (!data[1] && !data[2] && !data[3] && !data[4] && !data[5] && !data[6]) break; fprintf(stdout, "Collection #%d:\n", count ++); half = data[1] + data[2] * 2 + data[3] * 3 + data[4] * 4 + data[5] * 5 + data[6] * 6; if (half % 2 != 0) { fprintf(stdout, "Can't be divided.\n\n"); continue; } half /= 2; memset(ans, 0, (half + 1) * sizeof(int)); for (i = 0; i <= data[1]; i ++) ans[i] = 1; cur_max = data[1]; for (i = 2; i < 7; i ++) { if (ans[half]) break; data[i] = data[i] * i > half ? half / i : data[i]; cur_max += data[i] * i; if (cur_max > half) cur_max = half; k = 0; while (tpe[k + 1] - 1 <= data[i]) { for (j = cur_max; j > data[1] && j - tpe[k] * i>= 0; j --) if (!ans[j]) ans[j] = ans[j - tpe[k] * i]; k ++; } for (j = cur_max; j > data[1] && j - (data[i] - tpe[k] + 1) * i >= 0; j --) if (!ans[j])//特别注意要判断是否为1,开始调了好久,就因为这个没判断啊! ans[j] = ans[j - (data[i] - tpe[k] + 1) * i]; } if (ans[half]) fprintf(stdout, "Can be divided.\n\n"); else fprintf(stdout, "Can't be divided.\n\n"); }