题意:
将n根小木棒拼成若干根长度相同的木棒。求这些长度相同的木棒可能的最小长度。
思路:
深搜+剪枝。
剪枝1:不在同一位置多次尝试使用长度相同的木棒。
剪枝2:不用交换拼木棒的顺序(即替换第i木棒的第一个棒子是没有用的)。
剪枝3:不要希望通过仅仅替换已拼好棍子的最后一根木棒就能够改变失败的局面。
剪枝4:拼每一根棍子的时候,应该确保已经拼好的部分,长度是从长到短排列的.排除办法:每次找一根木棒的时候,只要这不是一根棍子的第一条木棒,就不应该从下标为0的木棒开始找,而应该从刚刚(最近)接上去的那条木棒的下一条开始找。
实现:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 int a[65], n, last; 8 bool used[65]; 9 10 bool cmp(const int & a, const int & b) 11 { 12 return a > b; 13 } 14 15 bool dfs(int r, int m, int l) 16 { 17 if (m == 0) 18 { 19 if (r == 0) 20 return true; 21 return dfs(r, l, l); 22 } 23 int start = 0; //剪枝4 24 if (m != l) 25 start = last + 1; 26 for (int i = start; i < n; i++) 27 { 28 if (!used[i] && m >= a[i]) 29 { 30 if (i != 0 && used[i - 1] == false && a[i] == a[i - 1]) //剪枝1 31 continue; 32 used[i] = true; 33 last = i; 34 if (r > 0 && dfs(r - 1, m - a[i], l)) 35 return true; 36 used[i] = false; 37 if (m == l || a[i] == m) // 剪枝2,3 38 return false; 39 } 40 } 41 return false; 42 } 43 44 int main() 45 { 46 while (cin >> n, n) 47 { 48 int sum = 0; 49 for (int i = 0; i < n; i++) 50 { 51 scanf("%d", &a[i]); 52 sum += a[i]; 53 } 54 sort(a, a + n, cmp); 55 bool flag = false; 56 for (int i = 1; i <= sum / 2; i++) 57 { 58 if (sum % i) 59 continue; 60 memset(used, 0, sizeof(used)); 61 if (dfs(n, i, i)) 62 { 63 flag = true; 64 cout << i << endl; 65 break; 66 } 67 } 68 if (!flag) 69 cout << sum << endl; 70 } 71 return 0; 72 }
总结:
1.要选择合适的搜索顺序
如果一个任务分为 A, B, C…..等步骤(先后次序无关),要优先尝试可能性少的步骤
2.要发现表面上不同,实质相同的重复状态,避免重复的搜索
3.要根据实际问题发掘剪枝方案