题意:给出n根小棒的长度stick[i],已知这n根小棒原本由若干根长度相同的长木棒(原棒)分解而来。求出原棒的最小可能长度。
先说先具体的实现:求出总长度sum和小棒最长的长度max,则原棒可能的长度必在max~sum之间,然后从小到大枚举max~sum之间能被sum整除的长度len,用dfs求出所有的小棒能否拼凑成这个长度,如果可以,第一个len就是答案。
下面就是关键的了,就是这道题dfs的实现和剪枝的设计:
1.以一个小棒为开头,用dfs看看能否把这个小棒拼凑成len长,如果可以,用vis[i]记录下用过的小棒,然后继续以另外一个小棒为开头,以此类推。
2.小棒的长度从大到小排序,这个就不解释了。
3.如果当前最长的小棒不能拼成len长,那么就返回前一步,更改前一步的最长小棒的组合情况(这里不能是全部退出),不用再继续搜索下去了。
4.最重要的,就是比如说17,9,9,9,9,8,8,5,2……如果当前最长小棒为17,它与第一个9组合之后dfs发现不能拼成len,那么17就不用和后面所有的9组合了,而直接和8开始组合。这个剪枝直接从TLE到16MS,很强大。
#include <iostream> #include <cstring> #include <algorithm> using namespace std; int s[100],vis[100]; int n,num,len; int cmp(int a,int b) { return a>b; } int DFS(int cur,int k,int cnt) { if(cnt==num) return 1; if(cur==len) return DFS(0,0,cnt+1); int pre=0;//pre用来计算重复长度的木棍(剪枝) int i; for(i=k;i<n;i++) { if(vis[i]==0&&s[i]+cur<=len&&s[i]!=pre) { pre=s[i]; vis[i]=1; if(DFS(s[i]+cur,i+1,cnt)) break; vis[i]=0; if(k==0) return 0; } } if(i==n) return 0; else return 1; } int main() { while(cin>>n&&n!=0) { int sum=0; for(int i=0;i<n;i++) { cin>>s[i]; sum+=s[i]; } sort(s,s+n,cmp); for(len=s[0];len<=sum/2;len++) { if(sum%len==0) { num=sum/len; memset(vis,0,sizeof(vis)); if(DFS(0,0,0)) break; } } if(len>sum/2) cout<<sum<<endl; else cout<<len<<endl; } return 0; }