zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Click和Command事件的区别是什么
    后台取不到repeater里的checkbox选中状态 和 checkbox 值
    asp.net 控件生命周期 内的执行步骤
    采购流程
    matlab练习程序(图像放大/缩小,双立方插值)
    matlab练习程序(获取鼠标坐标)
    matlab练习程序(区域填充算法,队列版)
    matlab练习程序(二值图像连通区域标记法,两步法)
    matlab练习程序(寻找凸包,Graham扫描法)
    matlab练习程序(图像旋转,双线性插值)
  • 原文地址:https://www.cnblogs.com/dongsheng/p/3036160.html
Copyright © 2011-2022 走看看