首页 > 编程语言 > 详细

Enum:Smallest Difference(POJ 2718)

时间:2015-10-03 15:30:21      阅读:194      评论:0      收藏:0      [点我收藏+]

                  技术分享

                  最小的差别

  题目大意:输入一串数字,问你数字的组合方式,你可以随意用这些数字组合(无重复)来组合成一些整数(第一位不能为0),然后问你组合出来的整数的差最小是多少?

  这一题可以用枚举的方法去做,这里我就用枚举的方法去做吧。

  首先这一题的限制是1000ms,我们知道全枚举的时间肯定是超时的,所以我们必须裁枝,我们这样看,我们得到的最小值,必须是两个数差别最小的时候才会出现,所以这里我们可以只用枚举lenth/2时候的数串的情况,这样就降低了很多的复杂度。

  但是这样还是不够,但是我们知道,如果我们规定枚举的子串A一定比子串B大,则当枚举第一个的时候,如果枚举第二个的数的某个位(比如枚举012467,子串A为204,当子串B的百位枚举到7的时候)我们就可不必往十位和各位枚举了)这样也会缩减很多时间,还要把最高位是0的情况排除,这也是必须做的情况

  当然,这一题还有一些很特殊的情况我们要考虑,比如当只输入01的时候,我们可能得不到结果,因为这个时候ans可能还是INF,这些情况要单独考虑

  

 1 #include <iostream>
 2 #include <functional>
 3 #include <algorithm>
 4 
 5 using namespace std;
 6 
 7 //全部用全局定义,防止递归太深产生MLE
 8 static bool used_A[10];
 9 static bool used_B[10];
10 static int input[10];
11 static int zero_set[6] = { 1, 10, 100, 1000, 10000, 100000 };//补0最多补5个
12 static int valueA, cutA, cutB, ans, length;
13 
14 void DFS_A(const int, const int);
15 void DFS_B(const int, const int);
16 
17 int main(void)//双DFS法
18 {
19     int case_sum;
20     char tmp;
21     while (~scanf("%d", &case_sum))
22     {
23         getchar();//消掉回车    
24         for (int i = 0; i < case_sum; i++)
25         {
26             length = 0;
27             while ((tmp = getchar()) != \n)
28             {
29                 if (tmp !=  )
30                     input[length++] = tmp - 0;
31             }
32             ans = INT_MAX;
33             cutA = length / 2;//length要砍一半
34             cutA = cutA>length - cutA ? cutA : length - cutA;
35             cutB = length - cutA;
36             valueA = 0;
37             
38             memset(used_A, 0, sizeof(used_A));
39 
40             DFS_A(0, 0);
41             if (ans != INT_MAX)
42                 cout << ans << endl;
43             else//这个情况是针对x 0的情况的
44                 cout << valueA << endl;
45         }
46     }
47     return 0;
48 }
49 
50 void DFS_A(const int sumA, const int level)
51 {
52     //函数DFS_A,枚举数段A的所有可能,默认A的大小要比B的大
53     if (sumA == 0 && level == 1)//出现第一位是0的情况,不用再枚举下去了
54         return;
55     if (level== cutA)
56     {
57         valueA = sumA;
58         memset(used_B, 0, sizeof(used_B));
59         DFS_B(0, 0);
60     }
61     else if (level < cutA)
62     {
63         for (int i = 0; i < length; i++)
64         {
65             if (!used_A[i])
66             {
67                 used_A[i] = 1;
68                 DFS_A(sumA * 10 + input[i], level + 1);
69                 used_A[i] = 0;//回溯
70             }
71         }
72     }
73 }
74 
75 void DFS_B(const int sumB, const int level)
76 {
77     //函数DFS_B,枚举数段B的所有可能,并对数值进行裁枝
78     if (sumB == 0 && level == 1)//出现第一位是0的情况,不用再枚举下去了
79         return;
80     int tmp = sumB * zero_set[cutB - level];
81     if (tmp >= valueA)//默认A的大小要比B的大
82         return;
83     else if (cutB == level)
84         ans = min(ans, valueA - sumB);
85     else if (level < cutB)
86     {
87         for (int i = 0; i < length; i++)
88         {
89             if (!used_A[i] && !used_B[i])
90             {
91                 used_B[i] = 1;
92                 DFS_B(sumB * 10 + input[i], level + 1);
93                 used_B[i] = 0;//回溯
94             }
95         }
96     }
97 }

  但是这一份代码

技术分享

  在评测机跑了875MS,我不开心,怎么会那么慢?

  其实一开始参考了这里http://blog.csdn.net/z309241990/article/details/19548297的方法,回过头来再看别人的判断条件

 1 void DFS_B(const int sumB, const int level)
 2 {
 3     //函数DFS_B,枚举数段B的所有可能,并对数值进行裁枝
 4     if (sumB == 0 && level == 1)//出现第一位是0的情况,不用再枚举下去了
 5         return;
 6     int tmp = sumB * zero_set[cutB - level];
 7     if (tmp >= valueA
 8         && (ans <= abs(valueA - tmp) && level > 0))
 9         return;
10     else if (cutB == level)
11         ans = min(ans, abs(valueA - sumB));
12     else if (level < cutB)
13     {
14         for (int i = 0; i < length; i++)
15         {
16             if (!used_A[i] && !used_B[i])
17             {
18                 used_B[i] = 1;
19                 DFS_B(sumB * 10 + input[i], level + 1);
20                 used_B[i] = 0;//回溯
21             }
22         }
23     }
24 }

 

  技术分享

  很好变成375MS,减了差不多一半

  后来想了一下,我这个tmp>=valueA真是画蛇添足,根本就不需要,其实ans<=min(ans,abs(valueA-sumB))这个条件就已经排除了对称的情况了

  比如如果一开始已经出了A:176和B:204的差值28,那么下一次A:204和B:167(注意我的顺序),就直接不用枚举176,可能你会问,不是还会有176的时候的更小的值吗?可是这个情况,绝对会被对称的情况优先排除,所以是不用考虑的

  最后代码:

  

 1 #include <iostream>
 2 #include <functional>
 3 #include <algorithm>
 4 
 5 using namespace std;
 6 
 7 //全部用全局定义,防止递归太深产生MLE
 8 static bool used_A[10];
 9 static bool used_B[10];
10 static int input[10];
11 static int zero_set[6] = { 1, 10, 100, 1000, 10000, 100000 };//补0最多补5个
12 static int valueA, cutA, cutB, ans, length;
13 
14 void DFS_A(const int, const int);
15 void DFS_B(const int, const int);
16 
17 int main(void)//双DFS法
18 {
19     int case_sum;
20     char tmp;
21     while (~scanf("%d", &case_sum))
22     {
23         getchar();//消掉回车    
24         for (int i = 0; i < case_sum; i++)
25         {
26             length = 0;
27             while ((tmp = getchar()) != \n)
28             {
29                 if (tmp !=  )
30                     input[length++] = tmp - 0;
31             }
32             ans = INT_MAX;
33             cutA = length / 2;//length要砍一半
34             cutB = length - cutA;
35             memset(used_A, 0, sizeof(used_A));
36 
37             DFS_A(0, 0);
38             if (ans != INT_MAX)
39                 cout << ans << endl;
40             else//这个情况是针对x 0的情况的
41                 cout << valueA << endl;
42         }
43     }
44     return 0;
45 }
46 
47 void DFS_A(const int sumA, const int level)
48 {
49     //函数DFS_A,枚举数段A的所有可能,默认A的大小要比B的大
50     if (sumA == 0 && level == 1)//出现第一位是0的情况,不用再枚举下去了
51         return;
52     if (level== cutA)
53     {
54         valueA = sumA;
55         memset(used_B, 0, sizeof(used_B));
56         DFS_B(0, 0);
57     }
58     else if (level < cutA)
59     {
60         for (int i = 0; i < length; i++)
61         {
62             if (!used_A[i])
63             {
64                 used_A[i] = 1;
65                 DFS_A(sumA * 10 + input[i], level + 1);
66                 used_A[i] = 0;//回溯
67             }
68         }
69     }
70 }
71 
72 void DFS_B(const int sumB, const int level)
73 {
74     //函数DFS_B,枚举数段B的所有可能,并对数值进行裁枝
75     if (sumB == 0 && level == 1)//出现第一位是0的情况,不用再枚举下去了
76         return;
77     int tmp = sumB * zero_set[cutB - level];
78     if (ans <= abs(valueA - tmp) && level > 0)//如果补0以后大小都比ans大了,不用再搜了
79         return;
80     else if (cutB == level)
81         ans = min(ans, abs(valueA - sumB));
82     else if (level < cutB)
83     {
84         for (int i = 0; i < length; i++)
85         {
86             if (!used_A[i] && !used_B[i])
87             {
88                 used_B[i] = 1;
89                 DFS_B(sumB * 10 + input[i], level + 1);
90                 used_B[i] = 0;//回溯
91             }
92         }
93     }
94 }

技术分享

  这个已经很快了

  但是这个还可以用贪心去做,可以是0ms,的确非常快,但是做起来异常复杂容易出错。

Enum:Smallest Difference(POJ 2718)

原文:http://www.cnblogs.com/Philip-Tell-Truth/p/4853362.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!