1. 程式人生 > >HDU 1059 Dividing

HDU 1059 Dividing

 Dividing

  Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value. 
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,不進行優化一定會超時。所以我們在拆分大理石的時候進行二進位制優化。

二進位制優化:   假設某一價值的大理石有100塊,我們並不需要將100塊大理石全部加入運算陣列,我們將100拆分為數個小於100的數字,使這些數字可以構成1 ~ 100中任意一個數字。這裡用到了一個數論的小知識:1,2,4 ~ 2^n 可以組成1 ~ 2^(n + 1) - 1之間的任意數,100便可分解為1,2,4,8,16,32,(取2的n次冪(二進位制數),不能取到64,因為拆分100的話所有分解的數加起來不能超過100) 37(前面取到的1 ~ 32已經可以表示63(2^(5 + 1) - 1)以內所有的數了,那麼再補上100 - 63 = 37這個數後就可以取到所有1 ~ 100的數了)。繼續分析,假設這100塊大理石價值都為2,那我們就可用到我們分解出來的數字,根據這些數字我們把100塊大理石分解1塊,2塊,4塊,8塊,16塊,32塊,37塊這7組,這樣我們便可以把100塊價值為2的大理石看成價值為2,4,8,16,32,64,74的7塊大理石。將這7塊大理石加入運算陣列在運算時比起100塊大理石就要節約很多時間。   至於為什麼1,2,4,8,16,32,37可以代替100,這很簡單,之前已經寫過了新的陣列可以表示1 ~ 100所有陣列,那麼在運算中,我們如果需要拿5塊價值1的大理石,在這裡就和拿一塊價值為1的大理石與一塊價值為4的大理石有同樣效果,拿其他所有1 ~ 100的數皆是這個道理。   dp記錄容量(最大價值)為 j 的揹包可以裝載大理石的最大價值。marbles記錄優化後每塊大理石的的價值。   動態轉移方程:dp[ j ] = max(dp[ j ], dp[ j - marbles[ i ] ] + marbles[ i ])   
 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 }