HDU 1059 Dividing
Dividing
Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.
Input
Each line in the input describes one collection of marbles to be divided. The lines consist of six non-negative integers n1, n2, ..., n6, where ni is the number of marbles of value i. So, the example from above would be described by the input-line ``1 0 1 2 0 0''. The maximum total number of marbles will be 20000.
The last line of the input file will be ``0 0 0 0 0 0''; do not process this line.
Output
For each colletcion, output ``Collection #k:'', where k is the number of the test case, and then either ``Can be divided.'' or ``Can't be divided.''.
Output a blank line after each test case.
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.
解题思路:
本题有价值为1~6的6种大理石各若干块,要求将所有的大理石对半分给Marsha和Bill。本题有多组数据,每组数据包括6个整数,分别为价值为1 ~ 6的大理石的个数,以6个0为输入结尾。
基本解题思想为动态规划01背包,背包容量为总价值的一半,背包内容物价值为大理石块的价值,如果容量为总价值一半的背包其最大内容物价值正好也为总价值的一半时可以平分。
本题不能将大理石拆分单块,因为每种大理石最多有20000块,最坏情况下大理石总量高度120000块背包最大容量为420000,01背包时间复杂度为O(V * N),而题目给出的时间限制为1000ms,不进行优化一定会超时。所以我们在拆分大理石的时候进行二进制优化。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 1e6+100; 4 int marbles[maxn]; //记录拆分后每块大理石的价值 5 int dp[maxn]; 6 int n[7]; 7 int tot = 0; //tot记录所有大理石总价值 8 int main() 9 { 10 int t = 1; 11 while(scanf("%d%d%d%d%d%d", &n[1], &n[2], &n[3], &n[4], &n[5], &n[6]) != EOF && (n[1] + n[2] + n[3] + n[4] + n[5] + n[6])){ 12 //输入每种大理石的数量 13 int cnt = 1,tot = 0; 14 for(int i = 1; i <= 6; i++){ //二进制拆分每一个价值的大理石 15 int num = n[i]; 16 int j = 1; 17 if(num != 0){ 18 while(num > j){ 19 //所有拆分后数字的和不超过num 20 //我们可以每拆分一个数就用num减去它直到num小于想要拆分的下一个数 21 marbles[cnt++] = j * i; //将拆分后的价值计入marbles 22 tot += marbles[cnt - 1]; //记入总价值 23 num -= j; //num减去当前拆分的数字 24 j *= 2; //下一个要拆分的数字为当前数字的两倍 25 } 26 marbles[cnt++] = num * i; //最后补上差的数字,并记录其组成的价值 27 tot += marbles[cnt - 1]; //记入总价值 28 } 29 } 30 cnt--; 31 if(tot % 2 != 0){ //总价值不能被2整出则一定不能平分 32 printf("Collection #%d:\nCan't be divided.\n\n", t); 33 t++; 34 continue; 35 } 36 tot /= 2; //记录总价值的一半 37 memset(dp, 0, sizeof(dp)); //初始化dp为0 38 for(int i= 0; i <= cnt; i++){ //01背包遍历大理石优化后的块数 39 for(int j = tot; j >= marbles[i]; j--){ //逆序遍历背包容量 40 dp[j] = max(dp[j], dp[j - marbles[i]] + marbles[i]); //动态转移方程 41 } 42 } 43 if(dp[tot] == tot) //正好平分 44 printf("Collection #%d:\nCan be divided.\n\n", t); 45 else 46 printf("Collection #%d:\nCan't be divided.\n\n", t); 47 t++; 48 } 49 50 return 0; 51 }