Description
Input
Output
Sample Input
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
Sample Output
6 5
Source
Translator
题解:
令InitLen为所求的最短原始棒长,maxlen为给定的棒子堆中最长的棒子,sumlen为这堆棒子的长度之和,那么InitLen必定在范围[maxlen,sumlen]中
根据棒子的灵活度(棒子越长,灵活度越低) DFS前先对所有棒子降序排序
剪枝:
1、 由于所有原始棒子等长,那么必有sumlen%Initlen==0;
2、 若能在[maxlen,sumlen-InitLen]找到最短的InitLen,该InitLen必也是[maxlen,sumlen]的最短;若不能在[maxlen,sumlen-InitLen]找到最短的InitLen,则必有InitLen=sumlen;
3、 由于所有棒子已降序排序,在DFS时,若某根棒子不合适,则跳过其后面所有与它等长的棒子;
4、 最重要的剪枝:对于某个目标InitLen,在每次构建新的长度为InitLen的原始棒时,检查新棒的第一根stick[i],若在搜索完所有stick[]后都无法组合,则说明stick[i]无法在当前组合方式下组合,不用往下搜索(往下搜索会令stick[i]被舍弃),直接返回上一层
附上代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 111; int a[N],vis[N]; int num,tem,n; // num:原木块的数量, tem: 原木块的最小长度; n:切断后木块的数量 bool dfs(int cnt,int len,int index) //每次从第index木头拼凑,每块木头都得用上。枚举后就不会再用到它了 { if(cnt==num) return true; // 如果能拼凑到num块木头 for(int i=index;i<=n;i++) { if(vis[i]) continue; vis[i]=1; if(len+a[i]==tem) { if(dfs(cnt+1,0,index+1)) return true; // 能够拼凑成一块木头...木头数量+1,len更新为0 else { vis[i]=0; return false; } } else if(len+a[i]<tem) { if(dfs(cnt,len+a[i],index)) return true; else vis[i]=0; if(len==0) return false; // 第一块木头不能别拼凑,以后的木块都不用再枚举了... while(a[i]==a[i+1]) i++; //根节点后面相同的节点(木头)不用再枚举了 } else vis[i]=0; } return false; } int main() { int i,j,Max,sum; while(scanf("%d",&n)==1&&n) { Max=sum=0; for(i=1;i<=n;i++) { scanf("%d",&a[i]); Max=max(Max,a[i]); sum+=a[i]; } sort(a+1,a+1+n,greater<int>()); //木块从大到小排序,因为小木块比大木块具有灵活性.。。。 for(tem=Max;tem<=sum;tem++) { //ans从最长的木块枚举到总长 if(sum%tem) continue; //木块总长是ans的倍数 num=sum/tem; memset(vis,0,sizeof(vis)); if(dfs(0,0,1)) break; } printf("%d\n",tem); } return 0; } // 这题能真正搞懂。才会是dfs入门,理解回溯,树的深度 ... 递归时的种种问题。。。。。 //好题,mark