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 }