POJ 1011 Sticks
题意:一开始你有若干个等长的木棍,后来把他们随机砍成几小段,问一开始的木棍最短多长。
解法:DFS + 剪枝。悲剧的发现自己已经不太会搜索了。
代码:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string> #include<string.h> #include<math.h> #include<limits.h> #include<time.h> #include<stdlib.h> #include<map> #include<queue> #include<set> #include<stack> #include<vector> #define LL long long using namespace std; int n, s, len; bool vis[70]; int stick[70]; bool cmp(int a, int b) { return a > b; } bool dfs(int remlen, int pos, int num)//remlen是当前拼的大木棍剩余长度,pos是枚举到哪一个小木棍,num是拼到哪一根大木棍 { if(num == s)//剪枝,如果已经拼到最后一根大木棍则一定能拼成,不需要搜索下去 return true; if(remlen == 0)//剪枝,当一根大木棍拼好,找到第一根未使用的小木棍进行搜索 { int i = 0; while(vis[i]) i++; vis[i] = true; if(dfs(len - stick[i], i, num + 1)) return true; vis[i] = false; return false; } for(int i = pos + 1; i < n; i++) { if(!vis[i]) { if(!vis[i - 1] && (stick[i] == stick[i - 1]))//剪枝,如果前一根小木棍没有被使用而且前一根小木棍跟当前小木棍长度相同则没有必要搜索下去 continue; if(!vis[i] && (remlen >= stick[i])) { vis[i] = true; if(dfs(remlen - stick[i], i, num)) return true; vis[i] = false; if(stick[i] == remlen) break; } } } return false; } int main() { while(~scanf("%d", &n) && n) { int maxx = 0, sum = 0; for(int i = 0; i < n; i++) { scanf("%d", &stick[i]); maxx = max(maxx, stick[i]); sum += stick[i]; } sort(stick, stick + n, cmp);//剪枝,从大到小排序 bool flag = true; for(int i = maxx; i <= sum / 2; i++)//原来木棍的长度一定是木棍和的因数,所以只需要枚举到sum / 2, 如果在这个范围内没有枚举到则答案是木棍长度的和 { if(sum % i)//木棍长度是木棍和的因数 continue; memset(vis, 0, sizeof vis); s = sum / i; len = i; vis[0] = true; if(dfs(len - stick[0], 0, 1)) { flag = false; printf("%d\n", i); break; } } if(flag) printf("%d\n", sum); } return 0; }