南昌理工学院acm集训队
搜索算法是利用计算机的高性能来有目的的穷举一个问题解空间的部分或所有的可能情况,从而求出问题的解的一种方法。现阶段一般有枚举算法、深度优先搜索、广度优先搜索、A*算法、回溯算法、蒙特卡洛树搜索、散列函数等算法。在大规模实验环境中,通常通过在搜索前,根据条件降低搜索规模;根据问题的约束条件进行剪枝;利用搜索过程中的中间解,避免重复计算这几种方法进行优化。
我们今天只讲深搜和广搜!
深搜原理
深搜,顾名思义,是深入其中、直取结果的一种搜索方法。
如果深搜是一个人,那么他的性格一定倔得像头牛!他从一点出发去旅游,只朝着一个方向走,除非路断了,他绝不改变方向!除非四个方向全都不通或遇到终点,他绝不后退一步!
深搜一般用于整个图的遍历,它一般用于求多个解的情况,例如八皇后问题就是一个很典型的深搜问题,它要求的是所有符合要求的棋子摆法,也就是要将整个棋盘都遍历一遍。
实现方式:递归和非递归(用栈实现)。栈的存取方式为先进后出,即每次选择栈顶元素往下搜索。
深搜优缺点
- 优点
1、能找出所有解决方案
2、优先搜索一棵子树,然后是另一棵,所以和广搜对比,有着内存需要相对较少的优点 - 缺点
1、要多次遍历,搜索所有可能路径,标识做了之后还要取消。
2、在深度很大的情况下效率不高
全排列:
#include<iostream>
using namespace std;
const int N=10;
int path[N],vis[N]={0};
int n;
int sum=0;
void dfs(int x)
{
if(x==n)
{
for(int i=0;i<n;i++)
printf("%d ",path[i]);
sum++;
printf("\n");
}
else
{
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
vis[i]=1;
path[x]=i;
dfs(x+1);
vis[i]=0;
}
}
}
}
int main()
{
scanf("%d",&n);
dfs(0);
printf("%d\n",sum);
return 0;
}
n皇后问题:
#include<iostream>
using namespace std;
const int N=20;
int a[N];
int li[N]={0},dg[N]={0},udg[N]={0};
int sum,n;
void dfs(int x)
{
if(x==n)
{
sum++;
for(int i=0;i<n;i++)
printf("%-5d",a[i]);
printf("\n");
}
else
{
for(int i=1;i<=n;i++)
{
if(li[i]==0 && dg[x+i]==0 && udg[x-i+n]==0)
{
li[i]=1;
dg[x+i]=1;
udg[x-i+n]=1;
a[x]=i;
dfs(x+1);
li[i]=0;
dg[x+i]=0;
udg[x-i+n]=0;
a[x]=0;
}
}
}
}
int main()
{
scanf("%d",&n);
dfs(0);
printf("%d\n",sum);
return 0;
}
这些都是比较简单的深搜问题。
广搜原理
广搜,顾名思义,是多管齐下、广撒网的一种搜索方法
如果广搜是一个人,那么她一定很贪心,而且喜新厌旧!她从一点出发去旅游,先把与起点相邻的地方全部游览一遍,然后再把与她刚游览过的景点相邻的景点全都游览一边……一直这样,直至所有的景点都游览一遍。
广搜一般是用于求图的最短路径,比如迷宫中走到某一点的最短距离,或者某个字符串交换达到目标字符串的最少次数,解的个数一般是为单一的,可以把搜索的整个过程想象成一棵树,要求的解就是其中的某一个节点,且距离根节点是最近的。
实现方式:用队列存储每一个子节点,由于队列的存取方式是先进先出,所以很符合广搜的要求,每求出一种情况,将其推入队列中,若当前节点的子节点都已求出,直接将队列的头取出继续求就行。
广搜优缺点
- 优点
1、对于解决最短或最少问题特别有效,而且寻找深度小
2、每个结点只访问一遍,结点总是以最短路径被访问,所以第二次路径确定不会比第一次短 - 缺点
1、内存耗费量大(需要开大量的数组单元用来存储状态)