棍子Sticks(poj_1011)[经典搜索]
【题意描述】
George用相同的长度棍子,将他们随机切成最多64个单位的长度,现在,他想回到原来的状态,但他忘了他原来的多少根,以及他们原本是多长。请帮助他和设计一个程序,计算最小的可能的原始长度。所有长度均大于零的整数。
【输入】
输入包含2行的块。第一行:切成多少根,最多有64根。第二行切成的每一根的长度。文件的最后一行包含零,表示结束。
【输出】
输出每行应包含原始棒的最小可能长度。
【输入样例】
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
【输出样例】
6 5
附源文件如下:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int Max = 65; 6 7 int n, len, stick[Max]; 8 bool flag, vis[Max]; 9 10 bool cmp(int a, int b) //排序从大到小的比较函数 11 { 12 return a > b; 13 } 14 15 void dfs(int dep, int now_len, int u) 16 { 17 if(flag) return; 18 if(now_len == 0) //目前长度为0,也就是说重新开始 19 { 20 int k = 0; 21 while(vis[k]) k ++; 22 vis[k] = true; 23 dfs(dep + 1, stick[k], k + 1); 24 vis[k] = false; 25 return; 26 } 27 if(now_len == len) 28 { // 当前长度为len,即又拼凑成了一根原棒 29 if(dep == n) flag = true; // 完成的标志:所有的n根小棒都有拼到了 30 else dfs(dep, 0, 0); 31 return; 32 } 33 for(int i = u; i < n; i ++) 34 if(!vis[i] && now_len + stick[i] <= len) 35 { 36 if(!vis[i-1] && stick[i] == stick[i-1]) continue; // 不重复搜索:最重要的剪枝 37 vis[i] = true; 38 dfs(dep + 1, now_len + stick[i], i + 1); 39 vis[i] = false; 40 } 41 } 42 43 int main() 44 { 45 freopen("p.in","r",stdin); 46 freopen("p.out","w",stdout); 47 while(scanf("%d", &n) && n != 0) 48 { 49 int sum = 0; 50 flag = false; 51 for(int i = 0; i < n; i ++) 52 { 53 scanf("%d", &stick[i]); 54 sum += stick[i]; 55 } 56 sort(stick, stick + n, cmp); // 从大到小排序 57 58 for(len = stick[0]; len < sum; len ++) 59 if(sum % len == 0) 60 { // 枚举能被sum整除的长度 61 memset(vis, 0, sizeof(vis)); //注意只能在这里进行初始化 62 dfs(0, 0, 0); 63 if(flag) break; 64 } 65 printf("%d\n", len); 66 } 67 return 0; 68 }