深搜回溯剪枝——小 木 棍!
导语
众所周知,深度优先搜索能帮助我们解决很多棘手的问题。
其中,回溯、剪枝算法作为深搜算法的姊妹,其地位显然是很高滴。
今天的这篇文章(题解)可以帮自己复习“回溯”算法和 小木棍 这道究极回溯剪枝题!
原题
(Source:CERC 1995)
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50 。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入格式
第一行为一个单独的整数 N 表示砍过以后的小木棍的总数。
第二行为 N 个用空格隔开的正整数,表示 N 根小木棍的长度。
输出格式
输出仅一行,表示要求的原始木棍的最小可能长度。
标签
深度优先搜索(dfs) 剪枝 回溯 恶心人(bushi)
分析
首先,这道题考虑用暴力算法去打,结果可想而知——T L E(Time Left Enough)
好吧,这道题的恶心之处已经显露出来了,所以我们正式想到——剪枝大法,回溯大法!
os:呃呃,好像,,不知道,,,从哪考虑丫。。。。
那就一步一步来!
第一步,想到这么多木棍杂乱无章,既然是复原木棍,那么复原回去的长度应该是有限度的。
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号