深搜,回溯,剪枝——小 木 棍!
导语
众所周知,深度优先搜索能帮助我们解决很多棘手的问题。
其中,回溯、剪枝算法作为深搜算法的姊妹,其地位显然是很高滴。
今天的这篇文章(题解)可以帮自己复习“回溯”算法和 小木棍 这道究极回溯剪枝题!
原题 (Source:CERC 1995)
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50 。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入格式
第一行为一个单独的整数 N 表示砍过以后的小木棍的总数。
第二行为 N 个用空格隔开的正整数,表示 N 根小木棍的长度。
输出格式
输出仅一行,表示要求的原始木棍的最小可能长度。
标签
深度优先搜索(dfs) 剪枝 回溯 恶心人(bushi)
分析
首先,这道题考虑用暴力算法去打,结果可想而知——T L E(Time Left Enough)
好吧,这道题的恶心之处已经显露出来了,所以我们正式想到——剪枝大法,回溯大法!
呃呃,好像,,不知道,,,从哪考虑丫。。。。
那就一步一步来!
第一步,想到这么多木棍杂乱无章,既然是复原木棍,那么复原回去的长度应该是有限度的。
1、最后的长度(答案)一定比这堆木棍中的最长的木棍要长(或者相等)
不然的话最长的那根木棍就没有办法拼了!
2、最后的长度一定小于等于所有短木棍的总长度
否则就永远不可能复原了
(所以要排序!)
第二步,明白一个事实:
短的木棍一定比长的木棍更有利用空间!
这个不用解释,很好理解。
(所以要从大到小排!)
第三步,为了更加省时间,再仔细观察
只有被现在的木棍总长度整除的数字才能有可能成为最终的答案!
而且为了更加更加节省时间,最后一次(总和/2)可以直接判定!(这个真的想不到)
然后,一个字
搜!
可是,正当你搜索时,Error,Error,Error...
你:......
好吧,还是不行....
开一个数组 used [ i ]表示已经搜过的木棍
开一个二分来快速找到要找的木棍
在dfs中一旦找到答案立即return
好不容易加上这三条...
终于......
A C 啦 !
The Whole Code:
#include<bits/stdc++.h> #define int long long inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } using namespace std; int n; const int MAXN=70; int a[MAXN]; bool cmp(int a,int b) { return a>b; } int cnt; int sum; int Next[MAXN]; int Count; bool used[MAXN]; int len; bool flag; void dfs(int id,int last, int remain) { int i; if(remain==0) { if(id==Count) { flag=true; return; }//find all and find for(i=1; i<=cnt; i++) { if(used[i]==0) { break; } } used[i]=1; dfs(id+1,i,len-a[i]); used[i]=0; if(flag) { return; }//find find find }//success int Left=1+last; int Right=cnt; int mid; while(Left<Right) { mid=(Left+Right)>>1; if(a[mid]<=remain) { Right=mid; } else { Left=mid+1; } }//find the Next(Left>=remain) for(i=Left; i<=cnt; i++) { if(used[i]==0) { used[i]=1; dfs(id,i,remain-a[i]);//search used[i]=0;//return if(flag==true) { return; }//last find and exit if(remain==a[i]||remain==len) { return; } i=Next[i];//find the Next mugun if(i==cnt) { return; }//find all but not find } } } signed main() { n=read(); int lin; for(int i=1; i<=n; i++) { lin=read(); if(lin>50) { continue; } else { a[++cnt]=lin; sum+=lin; } } sort(a+1,a+1+cnt,cmp); Next[cnt]=cnt; for(int i=cnt-1; i>=1; i--) { if(a[i]==a[i+1]) { Next[i]=Next[i+1]; } else { Next[i]=i; } } for(len=a[1]; len<=sum/2; len++) { if(sum%len!=0) { continue; } Count=sum/len; flag=false; used[1]=1; dfs(1,1,len-a[1]); used[1]=0; if(flag) { cout<<len; return 0; } } cout<<sum; return 0; }
最后
写文不易,点赞再走嘛~~

Update
2022/4/5 标题删改
-------------------------------------------
个性签名:そんなに形に拘らないの、大切なのは心よ
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!

浙公网安备 33010602011771号