【阅读部分】
搜索是OI之路上,人人必会的强大算法。自古便有名言:“暴力进省队”(实际上,很多考试你打好所有暴力就可以拿到不错的分数)。
在考场上,搜索常常是与正解的对拍板子(当然有时搜索就是正解),且一般搜索都会有20~30分。
而想要写好搜索,剪枝必不可少(有时出题人不会给纯暴力分)。
what's 剪枝?
总所周知:常用的搜索有Dfs和BfsDfs即深度优先搜索,它的进程近似一棵树(通常叫做Dfs树)
而剪枝,就是一种极其生动的比喻:把不会产生答案,不必要或是不符合条件的枝条剪去
剪枝的关键就在于:什么时候该剪,什么时候不该剪
剪枝的原则?
①正确性
②准确性
③高效性
常用的剪枝?
可行性剪枝,最优性剪枝,记忆化搜索,搜索顺序剪枝
介绍完剪枝,依旧上一道题目:
【题目部分】
小明爸爸是个木工,小明爸爸做家具时,经常需要某一长度的木条。该木条可以是一整条,也可以是由几个木条拼成。每次小明爸爸都要在一大堆的木条剩料中去拼一条需要的长度而耗费大量的时间。小明为了给爸爸排忧解难,便自告奋勇的接下这个任务。
当然,小明又不是神,他是解决不了这个问题的,他之所以这么信心十足,是因为他找到了学编程的你。
刚好你学完了搜索算法之一-----回溯,正好可以解决这个问题,
为了使问题简便,小明已经把剩料的长度都按顺序排成一行,并做了标记。
你要做的就是:按从左到右的顺序找出合适的木料,使得长度刚好是要求的长度。
如果没有解则输出“No Solution!”。
Input
输入数据的第一行为整数n和X。n(0 < n < 6100)表示共有n条木料.X表示要拼的长度(0 < x < 100000000).
接下去有一行n个整数s(0 < s < 1000000,表示剩料的长度)
Output
按顺序输出你找出来的木料的长度。
Sample Input 1
4 9 2 1 7 8Sample Output 1
2 7时间限制:
1000ms(我个人感觉还是很松的,这个时间qwq)
内存限制:
64MB
看到这道题,我的思路是:
其实有点收到这题的思路的启发(素数环),先用dfs对每个木料进行搭配,如果不符合条件就回溯一步
代码实现:
#include<bits/stdc++.h>
using namespace std;
long long n,x;
long long a[100001],c[100001];
bool b[100001],bj=0;
void print(int k){//打印函数
bj=1;
for(int i=1;i<=k;i++)
cout<<c[i]<<" ";
cout<<endl;
return ;
}
void dfs(int t,int y){//深搜
if(t>n)
return ;
for(int i=1;i<=n;i++){
if(!b[i]){
b[i]=1;
c[t]=a[i];
if(y+a[i]==x){//符合条件就输出
print(t);
break;
}
else{
dfs(t+1,y+a[i]);//否则继续选择木料
b[i]=0;
}
}
}
return ;
}
int main(){
cin>>n>>x;
for(int i=1;i<=n;i++)
cin>>a[i];
dfs(1,0);
if(!bj)
cout<<"No Solution!";
return 0;
}
一提交发现:稻花乡里说丰年,听取TLE声一片。(还有一个WA的hhhh)
然后,然后我就去做别的题了qwq
直到昨天,我差不多快学完了搜索,我回来一看发现我当时堪比弱智
我先来讲讲剪枝思路:
最优性剪枝:
一般要自己构造一个估值函数,由该估值函数计算上界和下界,最优性剪枝又是如何进行的呢?当我们处在搜索树的枝条上时,可以通过某种方法估算出该枝条上的所有解的评价函数的上界,即所谓估价函数。显然,大于当前保存的优度的下界,是该枝条上存在最优解的必要条件,否则就一定可以剪枝。所以,最优性剪枝也可以称为“上下界剪枝”。同时,我们也可以看到,最优性剪枝的核心问题就是估价函数的建立。
最优化剪枝里的估值函数(启发式搜索)等等内容,需要大家多查资料多做题。实践出真知。
以本题为例:
假设你已经处理到第pos根木条,已经挑的木条长度为 pin 。利用贪心思想(估值函数就是这个利用贪心思想):把剩下的所有木条长度用一个循环都加起来存到sx,如果sx+pin < X ,那么说明剩下的木条不管怎么选都是无法得到答案的,也没必要往下搜了,直接回溯到上一层。
懂了吗qwq?
不想画图.jpg
上代码:
#include<bits/stdc++.h>
using namespace std;
long long n,x,js,k=1,pin;
long long a[100001],c[100001];
bool b[100001],bj=0,gg;
void print(int xx){
for(int i=1;i<=xx;i++)
cout<<c[i]<<" ";
}
void read(){//读入函数,我不会写快读hhhh
cin>>n>>x;
for(int i=1;i<=n;i++)
cin>>a[i];
}
void dfs(long long k,long long js){
if(k>n)
return ;
if(js>x)
return ;
for(int i=1;i<=n;i++){
if(!b[i]){
pin+=a[i];
}
}
if(js+pin<x){//如果剩下的所有木料加起来都不够
pin=0;//归0一下
return ;//因为剩下的所有木料加起来都不够,所以直接回到上一步
}
if(js==x&&!gg){
print(k-1);//输出
bj=1;
gg=1;//标记一下已经找到结果,方便return后直接退出
return ;
}
else{
for(int i=1;i<=n;i++){
if(!b[i]){//如果这根木料没用过
b[i]=1;//用一下
c[k]=a[i];//储存木料长度
dfs(k+1,js+a[i]);//搜索下一条木料
if(gg){//找到方案后就退出
break;
}
b[i]=0;
c[k]=0;
}
}
}
return ;
}
int main(){
std::ios::sync_with_stdio(false);
read();
dfs(1,0);
if(bj==0)
cout<<"No Solution!";
return 0;
}
提交后:
——byebye<( ̄3 ̄)>