# 题意
一组长度相等的木棒,把他们随机砍断,使得每一节木棍的长度不超过50个单位,恢复到剪前的状态,不知道开始有多少根以及他们的长度,计算木棒可能的最小长度
# 题解
因为木棒是等长的所以最开始木棒的长度一定是所有木棒长度和约数,并且原始木棒的根数就等于sum/len
参数中所包含的状态应该有
1)已经拼好的原始木棒的数量
2)正在拼的原始木棒的当前长度
3)每个木棒的使用情况,即上一根拼接上的小木棍
剪枝:
1)优化搜索顺序 小木棍长度从大到小去枚举可以减少搜索树的分支
2)排除等效冗余
1. 限制先后加入一根原始木棒的长度是递减的。先拼上一根长度为x的木棍,再拼上一根长为y的木棍,与先y后x是等价的
2. 对于当前拼的大木棒,记录最近一次尝试拼的小木棒长度,如果这个不可以,就不再想改木棒中拼接其他和这个木棒长度相等的木棒
3. 如果在当前的大木棒中,尝试加入的第一个小木棍失败了,那么这个木棍加到第一个就不可行因为后面的都是没有拼的,空木棒是等价的
4. 如果当前小木棍拼上最后一根后,后面的递归搜索是失败的,那么这个搜索一定是失败的,因为用其他更小的木棒拼起来长度和他相等可以看作是和这个长度较长的是等价的,并且用1根肯定比用多根更优
剪枝的四个性质:
1) 同一根大木棒上面小木棒顺序的等效性
2) 等长木棍的等效性
3) 空木棒的等效性
4) 贪心
#include <bits/stdc++.h> using namespace std; const int N=70; int n; int len[N]; int sum,length; bool st[N]; bool dfs(int u,int le,int now){ //分别表示大木棒的个数、当前正在拼接的木棒的长度,上一个点操作完成后应该操作的下一个点 if(u*length == sum) return 1; if(le == length) return dfs(u+1,0,0); for(int i=now;i<n;i++){ if(st[i]) continue; int l=len[i]; //加入到当前大棒的过程 if(le+l<= length) { st[i]=1; if(dfs(u,le+l,i+1)) return 1; st[i]=0; //已经回溯,也就是从当前开始的分支失败 //剪枝2,如果是第一根木棍,直接回溯 if(!le) return 0; //剪枝3,如果是最后一根木棍,直接回溯 if(le+l==length) return 0; //剪枝4,不去搜索和当前已经失败长度相等的小木棍 int j=i; while(j<n && len[j]==l) j++; i=j-1; } } return 0; } int main(){ while(cin>>n,n){ sum=0,length=0; memset(st,0,sizeof st); for(int i=0;i<n;i++) { cin >> len[i]; if(len[i]>50) continue; sum+=len[i]; length=max(length,len[i]); } //剪枝1,优化搜索顺序,从大到小 sort(len,len+n); reverse(len,len+n); for(int i=0;i<n;i++) if(len[i]>50) st[i]=1; while(1){ if(sum % length==0 && dfs(0,0,0)){ cout<<length<<endl; break; } length++; } } return 0; }