AcWing:167. 木棒(dfs + 剪枝)
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。
然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。
请你设计一个程序,帮助乔治计算木棒的可能最小长度。
每一节木棍的长度都用大于零的整数表示。
注意: 数据中可能包含长度大于50的木棒,请在处理时忽略这些木棒。
输入格式
输入包含多组数据,每组数据包括两行。
第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。
第二行是截断以后,所得到的各节木棍的长度。
在最后一组数据之后,是一个零。
输出格式
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
输入样例:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
输出样例:
6
5
算法:dfs + 剪枝
题解:剪枝:1、优先搜索顺序(从大大小),优先尝试较长的木棒。
2、要求先后加入的木棒有单调性,因为你每根木棒都要用,之前加入和之后加入都是一个样。
3、当拼接一个新木棒时,我加入一个木棒,失败了,说明,之后用到这个木棒拼接一个新木棒的时候都会失败。
4、当我现在我要加入的木棒拼接上去,正好等于拼接的长度,但是拼接失败了,说明之后继续拼接下去也是失败。
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int maxn = 1e5+7; int arr[maxn]; int vis[maxn]; int k, cnt; bool cmp(int a, int b) { return a > b; } bool dfs(int stick, int cal, int last, int len) { if(stick > cnt) { //当所有木棒已经拼好时,搜索成功 return true; } if(cal == len) { //木棒已经拼好,去拼下一根 return dfs(stick + 1, 0, 0, len); } int fail = 0; //剪枝2,记录重复值 for(int i = last; i < k; i++) { if(!vis[i] && cal + arr[i] <= len && fail != arr[i]) { vis[i] = 1; if(dfs(stick, cal + arr[i], i + 1, len)) { return true; } vis[i] = 0; fail = arr[i]; if(cal == 0 || cal + arr[i] == len) { //剪枝3,剪枝4 return false; } } } return false; //当所有分支都尝试过,并且还没成功 } int main() { int n; while(~scanf("%d", &n) && n) { int sum = 0, max_len = 0, x; k = 0; for(int i = 1; i <= n; i++) { scanf("%d", &x); if(x > 50) { continue; } sum += x; max_len = max(max_len, x); arr[k++] = x; } sort(arr, arr + k, cmp); //剪枝1 int i; for(i = max_len; i <= sum; i++) { if(sum % i != 0) { continue; } cnt = sum / i; //获取木棒的数量 for(int j = 0; j < k; j++) { vis[j] = 0; } if(dfs(1, 0, 0, i)) { break; } } printf("%d\n", i); } return 0; }