小木棍
传送门:
P1120 小木棍 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路
看到数据范围一般来说就是用dfs了,同时肯定是需要剪枝的
先看DFS思路吧
无非两种思路:
-
枚举原木长度 凑相同长的原木来找可以凑多少根原木,原木数越大,单根原木长度越短(这里称原来没被锯断的木头为原木)
-
枚举原木长度 计算出每次如果有解的话原木数量为多少,然后凑原木数
两者虽然都最后与原木数息息相关,但是思路1存在诸多问题,比如什么时候是dfs的终止情况,什么时候应该回溯,这里如果我们枚举的时候从小到大枚举,不依靠原木数是可以以当前每一个木头都被用来组成原木而作为dfs终止条件的,但是带来的问题是这样做的话剪枝所带来的时间优化很少,我暂时是没想到的,而且判断种植的时间复杂度也高,显然是不合理的
第一点是刚刚看到这道题所想的,但是再转念一想很蠢!很蠢!很蠢!
第二点的优势就在于用类似于组合数的爆搜来做十分好懂,如果不卡常不看剪枝这道题就是普及-!
ok!开搜
//rep(i,a,b)==for(int i = a; i <=b; i++)
rep(i,a[1],sum){
//这边先对a数组做一个从大到小的排序,这样原木的最小长度至少也是这里木棍的最大长度,那么原木最大长度毋庸置疑是这里所有木棍之和。
//为什么?为什么先想着,这个点的思路下面AC Code里写了
if(sum%i==0){
//枚举i,如果正好有sum/i根原木这时候就是有可能有解。
cnt=sum/i;//如果有解,那么原木就有sum/i根(这边枚举的是原木长度i)
dfs(1,0,1,i);//开搜
}
}
void dfs(int x,int cur,int start,int len){
//x表示当前木头组成len的第几根,cur表示但当前已组成的木棍,start表示从第几根木棍开始,len就表示当前情况下的单根原木长度
//len表示当前单根原木长度
if(x>cnt){
//已找到可以组成的原木数>理想状况下的原木数就是终止条件
cout<<len;//输出len,因为len是从小到大开始循环的所以能找到的话就是答案
exit(0);//退出整个程序
//exit(0)和return 的区别:exit(0)直接退出整个程序,return 是终止当前函数
//这里找到答案就直接退出整个程序就行了,不用再return回溯浪费时间,这里也算一种剪枝
}
if(cur==len){
//已组成了一根原木,我们开始组成下一根,
dfs(x+1,0,1