NYOJ546 Divideing Jewels

 

Divideing Jewels

时间限制:1000 ms  |  内存限制:65535 KB
难度:4
 
描述

Mary and Rose own a collection of jewells. They want to split the collection among themselves so that both receive an equal share of the jewels. This would be easy if all the jewels had the same value, because then they could just split the collection in half. But unfortunately, some of the jewels are larger, or more beautiful than others. So, Mary and Rose start by assigning a value, a natural number between one and ten, to each jewel. Now they want to divide the jewels so that each of them gets the same total value. Unfortunately, they realize that it might be impossible to divide the jewels in this way (even if the total value of all jewels is even). For example, if there are one jewel 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 jewels.

 
输入
Each line in the input file describes one collection of jewels to be divided. The lines contain ten non-negative integers n1 , . . . , n10 , where ni is the number of jewels of value i. The maximum total number of jewells will be 10000.
The last line of the input file will be "0 0 0 0 0 0 0 0 0 0"; do not process this line.
输出
For each collection, output "#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.
样例输入
1 0 1 2 0 0 0 0 2 0
1 0 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
样例输出
#1:Can't be divided.

#2:Can be divided.


来源
第五届河南省程序设计大赛
代码一: 化为 0-1背包求解(还是自己的方法有问题)  TLE了
View Code
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 
 7 int sum[11], dp[100005];
 8 int tot;
 9 
10 void ZeroOneDp()
11 {
12     for(int i = 1; i <= 10; ++i)
13     {
14         for(int j = tot; j >= i; --j)
15         {
16             for(int k = 0; k <= sum[i]; ++k)
17             {
18                 if(j >= i*k && dp[j-i*k] + i*k > dp[j])// 注意这里一定要加上 j >= i*k这个条件,否则会 RuntimeError 
19                     dp[j] = dp[j-i*k] + i*k;
20             }
21         }
22             
23     }      
24 }
25 
26 int main()
27 {
28     for(int T = 1; ; ++T)
29     {
30         tot = 0;
31         for(int i = 1; i <= 10; ++i)
32         {
33             scanf("%d", &sum[i]);
34             tot += sum[i]*i;
35         }
36         if(!tot)
37             break;
38         if(tot & 1)  // 财宝总额为奇数的肯定不能平分 
39         {
40             printf("#%d:Can't be divided.\n\n", T);
41             continue;
42         }
43         tot >>= 1;
44         memset(dp, 0, sizeof(dp));
45         ZeroOneDp();
46         if(dp[tot] == tot)
47             printf("#%d:Can be divided.\n\n", T);
48         else
49             printf("#%d:Can't be divided.\n\n", T);
50     }    
51     return 0;
52 }       

代码二:  ----DFS(AC) 

View Code
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 
 7 int sum[11];
 8 int tot;
 9 bool flag;
10 
11 void DFS(int sumval) // sumval 记录当前搜索到的总价值量 
12 {
13     if(flag)   // 以flag作为标致,只要凑够就立即退出
14         return;
15     if(sumval == tot)
16     {
17         flag = true;
18         return;
19     }
20     for(int i = 10; i >= 1; --i)//   其实这样的递归和 01 背包的真是思想差不多
21     {                            
22         if(sum[i]>0 && sumval+i<=tot)  
23         {
24             sum[i]--;
25             DFS(sumval + i);
26             if(flag)
27                 return;
28         }        
29     }
30 }
31 
32 int main()
33 {
34     for(int T = 1; ; ++T)
35     {
36         tot = 0;
37         for(int i = 1; i <= 10; ++i)
38         {
39             scanf("%d", &sum[i]);
40             tot += sum[i]*i;
41         }
42         if(!tot)
43             break;
44         if(tot & 1)  // 财宝总额为奇数的肯定不能平分 
45         {
46             printf("#%d:Can't be divided.\n\n", T);
47             continue;
48         }
49         tot >>= 1;
50         flag = false;
51         DFS(0);
52         if(flag)
53             printf("#%d:Can be divided.\n\n", T);
54         else
55             printf("#%d:Can't be divided.\n\n", T);
56     }    
57     return 0;
58 }

代码三:虽然仍然是将完全背包转化为 0-1背包,但是其中巧妙的用到了二进制来优化问题(AC)

View Code
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 
 7 int sum[11], dp[100005];
 8 int tot;
 9 
10 int max(int a, int b)
11 {
12     return a > b ? a : b;
13 }
14 
15 int main()
16 {
17     int k;
18     for(int T = 1; ; ++T)
19     {
20         tot = 0;
21         for(int i = 1; i <= 10; ++i)
22         {
23             scanf("%d", &sum[i]);
24             tot += sum[i]*i;
25         }
26 
27         if(!tot)
28             break;
29 
30         if(tot & 1)  // 财宝总额为奇数的肯定不能平分
31         {
32             printf("#%d:Can't be divided.\n\n", T);
33             continue;
34         }
35             
36         tot >>= 1;
37         dp[0] = 0;
38         for(int i = 1; i <= tot; ++i)
39             dp[i] = -0x7fffffff;
40         for(int i = 1; i <= 10; ++i)
41         {
42             for(int j = tot; j >= i; --j)
43             {
44                 k = 1;
45                 int tmp = 0;
46                 
47                 /*
48                     下边这一点很奇怪, 本来按照二进制打包的思想,每次打包后都应从总数量中减去已经打包后的数量
49                 再用剩下的去打包(即每次打包后都应该  sum[i] -= k)。但是这样提交一直WA  ,
50                 后来就没有在每次打包后减去已经打包的数量(即每次k都和原来的总数量比较),这样打包的个数理论上肯定
51                 比上边的包多,但是也能凑足每个要求,可以提交,不知道为什么?????????
52                 */
53                 while(sum[i] >= k && j >= k*i)//转换为01背包,二进制思想优化
54                 {//(例:13本来可以分为 1(2^0)、2(2^1)、4(2^2)、6,就行了,因为1、2、4、6这四个数可以组合成1—13中的所有数字)
55                     dp[j] = max(dp[j], dp[j-k*i]+k*i);  //  但是这个程序只要  将 13  打包成  1, 2, 4, 8,5  可以提交。。。
56                     tmp += k;
57                     k <<= 1;
58                 }
59                 k = sum[i] - tmp;
60                 if(j >= k*i)
61                     dp[j] = max(dp[j], dp[j-k*i]+k*i);
62             }
63         }
64 
65         if(dp[tot] == tot)
66             printf("#%d:Can be divided.\n\n", T);
67         else
68             printf("#%d:Can't be divided.\n\n", T);
69     }
70     return 0;
71 }

 代码四:解决了代码三遇到的问题

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 
 7 int sum[11], dp[100005];
 8 int tot;
 9 
10 int max(int a, int b)
11 {
12     return a > b ? a : b;
13 }
14 
15 int main()
16 {
17     int k;
18     for(int T = 1; ; ++T)
19     {
20         tot = 0;
21         for(int i = 1; i <= 10; ++i)
22         {
23             scanf("%d", &sum[i]);
24             tot += sum[i]*i;
25         }
26 
27         if(!tot)
28             break;
29 
30         if(tot & 1)  // 财宝总额为奇数的肯定不能平分
31         {
32             printf("#%d:Can't be divided.\n\n", T);
33             continue;
34         }
35             
36         tot >>= 1;
37         dp[0] = 0;
38         for(int i = 1; i <= tot; ++i)
39             dp[i] = -0x7fffffff;
40         for(int i = 1; i <= 10; ++i)
41         {
42             k = 1;
43             while(k <= sum[i])
44             {
45                 for(int j = tot; j >= i*k; --j)
46                 {
47                     dp[j] = max(dp[j], dp[j-k*i]+k*i);
48                 }
49                 sum[i] -= k;
50                 k <<= 1; 
51             } 
52             for(int j = tot; j >= i*k; --j)
53             {
54                 dp[j] = max(dp[j], dp[j-sum[i]]+sum[i]);
55             }         
56         }
57 
58         if(dp[tot] == tot)
59             printf("#%d:Can be divided.\n\n", T);
60         else
61             printf("#%d:Can't be divided.\n\n", T);
62     }
63     return 0;
64 }
View Code
原文地址:https://www.cnblogs.com/dongsheng/p/3036160.html