Uva--307(回溯,超强剪枝)

2014-07-17 21:29:52

题意:经典的小木棍问题。问有这么n根小木棍,每根有自己的长度len(i)(<50),问让他们组合(长度相加),能组成的最小length,使每个组合的木棍长度均为length。

思路:暴力回溯,重在剪枝!由于这题并未告诉 n 的范围,所以要特意考虑剪枝(不给范围就是为了卡剪枝的吧QAQ)

剪枝:(1)将len(i)从大到小排序比从小到大排序更快,因为优先找大的len,更快达到所需的长度以减少递归层数。

   (2)枚举目标长度的范围控制在MAX(len(1),len(2),...,len(n))到SUM(len(1),len(2),..,len(n)),所有不能被sum整出的目标长度都要被跳过。

   (3)如果某根木棍和上一根木棍长度一样,而且上一根没有被选上,则这根木棍也不会被选上。

   (4)如果在组合过程中,采用第一截木棍DFS下去都不能拼成目标长度的话,说明前面的过程就有问题。(因为在之后的递归过程中这第一截木棍总要被用到)

   (5)如果当前组合成功拼成一根目标长度的木棍,而之后的递归却拼不成功,说明前面的过程就有问题。

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <cmath>
 6 using namespace std;
 7 
 8 int n,sum,tmax,s[100],used[100],ans;
 9 
10 bool Dfs(int num,int pos,int len){
11     if(num * ans == sum)
12         return true;
13     for(int i = pos; i < n; ++i){
14         if(used[i] || (i && s[i] == s[i - 1] && !used[i - 1])) continue;
15         if(len + s[i] < ans){
16             used[i] = 1;
17             if(Dfs(num,i + 1,len + s[i])) return true;
18             used[i] = 0;
19             if(len == 0) return false;
20         }
21         else if(len + s[i] == ans){
22             used[i] = 1;
23             if(Dfs(num + 1,0,0)) return true;
24             used[i] = 0;
25             return false;
26         }
27     }
28     return false;
29 }
30 
31 int main(){
32     while(scanf("%d",&n) == 1 && n){
33         sum = 0;
34         tmax = -1;
35         for(int i = 0; i < n; ++i){
36             scanf("%d",&s[i]);
37             sum += s[i];
38             tmax = max(tmax,s[i]);
39         }
40         sort(s,s + n,greater<int>());
41         memset(used,0,sizeof(used));
42         for(ans = tmax; ans <= sum; ++ans)
43             if(sum % ans == 0)
44                 if(Dfs(0,0,0))
45                     break;
46         printf("%d\n",ans);
47     }
48     return 0;
49 }
posted @ 2014-07-17 21:46  Naturain  阅读(425)  评论(0编辑  收藏  举报