一.搜索算法的概念及其种类:
1.概念:
利用计算机的高效性,穷举出问题的部分或者所有的解,是求解问题的一种方法。本质上是通过初始条件扩展出问题的“解答树”,去寻找问题的解。构建这样的树形模型主要由两个部分——控制结构(扩展节点的方式)和产生系统(扩展节点)。通过对每个节点不同方式的拓展去寻找目标解。
2.种类:
- 深度优先搜索(DFS)
- 广度优先搜索(BFS)
- 二分搜索
- 散列法(Hash)
二.算法实例:
1.深度优先搜索 :
深度优先搜索可用栈和递归实现,深度优先搜索和回溯法类似都属于穷举搜索,只是回溯大多可以写在dfs中,只是回溯法需要返回上一结点的状态,判断是否使用深度优先搜索需要考虑的因素有:
- 是否可以通过列出所有答案求解。
- 是否可以找到限制条件进行剪枝。
基础模板
void dfs()//参数用来表示状态
{
if(到达终点状态)
{
...//根据题意添加
return;
}
if(越界或者是不合法状态)
return;
if(特殊状态)//剪枝
return ;
for(扩展方式)
{
if(扩展方式所达到状态合法)
{
修改操作;//根据题意来添加
标记;
dfs();
(还原标记);
//是否还原标记根据题意
//如果加上(还原标记)就是 回溯法
}
}
}
例题
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int n,C[15],vis[3][30],tot;//三个标记分别记录列,主对角线,副对角线
void init()
{
memset(C,0,sizeof(C));
memset(vis,0,sizeof(vis));
tot=0;
}
void dfs(int cur)
{
if(cur==n) {
if(tot<3)
{
for(int i=0;i<n;i++)
printf("%d ",C[i]+1);
printf("\n");
}
tot++;
}
else
for(int i=0;i<n;i++) {
if(!vis[0][i]&&!vis[1][i+cur]&&!vis[2][cur-i+n])//每条主对角线行+列相等,每条副对角线行-列相等
{
C[cur]=i;
vis[0][i]=vis[1][i+cur]=vis[2][cur-i+n]=1;
dfs(cur+1);
vis[0][i]=vis[1][i+cur]=vis[2][cur-i+n]=0;//一定要还原,回溯的基本操作
}
}
}
int main()
{
while(scanf("%d",&n)!=EOF){
init();
dfs(0);
printf("%d",tot);
}
return 0;
}
- 素数环
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
int n,book[20],ans[20];
int isprime[40]={0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0};
void init(){
memset(book,0,sizeof(book));
memset(ans,0,sizeof(ans));
}
void dfs(int step){
if(step==n)
{
if(isprime[ans[n-1]+ans[0]])
{
for(int i=0;i<n;i++)
if(i!=n-1)
printf("%d ",ans[i]);
else
printf("%d",ans[i]);
printf("\n");
}
return;
}
for(int i=2;i<=n;i++)
{
if(!book[i]&&isprime[i+ans[step-1]])
{
ans[step]=i;
book[i]=1;
dfs(step+1);
book[i]=0;
}
}
}
int main()
{
int t=1;
while(scanf("%d",&n)!=EOF){
init();
printf("Case %d:\n",t++);
if(n==1){
printf("%d\n\n",1);
continue;
}
ans[0]=1;
book[1]=1;
dfs(1);
printf("\n");
}
return 0;
}
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int n,m,T,sx,sy,ex,ey,ans=0;
int mp[6][6],turn[4][2]={0,1,1,0,0,-1,-1,0};
void dfs(int x,int y)
{
if(x==ex&&y==ey)
ans++;
else
{
int nx,ny;
for(int i=0;i<4;i++)
{
nx=x+turn[i][0];
ny=y+turn[i][1];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&mp[nx][ny])
{
mp[nx][ny]=0;
dfs(nx,ny);
mp[nx][ny]=1;
}
}
}
}
int main()
{
memset(mp,1,sizeof(mp));
scanf("%d%d%d%d%d%d%d",&n,&m,&T,&sx,&sy,&ex,&ey);
for(int i=1;i<=T;i++)
{
int temp1,temp2;
scanf("%d%d",&temp1,&temp2);
mp[temp1][temp2]=0;
}
mp[sx][sy]=0;
dfs(sx,sy);
printf("%d",ans);
return 0;
}
- HDU Robot Motion(给定方向搜索类似模拟)
在初始化book数组时,如果初始化为0,则对于一进入地图就是loop的数据就会出现错误,因为第一次经过入口时给入口的值为0,以至于检测是否循环时发生错误。以后对标记数组初始化时,将其初始化为-1。
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int n,m,sy,book[15][15],answer1,answer2,flag=0;
char mp[15][15];
void dfs(int x,int y,int step)
{
if(x<0||x>n-1||y<0||y>m-1)
{
answer1=step;
return;
}
if(book[x][y]!=-1)
{
answer1=book[x][y];
answer2=step-book[x][y];
flag=1;
return;
}
book[x][y]=step;
if(mp[x][y]=='N')
dfs(x-1,y,step+1);
else if(mp[x][y]=='S')
dfs(x+1,y,step+1);
else if(mp[x][y]=='W')
dfs(x,y-1,step+1);
else if(mp[x][y]=='E')
dfs(x,y+1,step+1);
}
int main()
{
while(scanf("%d%d%d",&n,&m,&sy),n!=0||m!=0||sy!=0)
{
memset(book,-1,sizeof(book));
memset(mp,'0',sizeof(mp));
for(int i=0;i<n;i++)
scanf("%s",mp[i]);
dfs(0,sy-1,0);
if(flag)
printf("%d step(s) before a loop of %d step(s)\n",answer1,answer2);
else
printf("%d step(s) to exit\n",answer1);
answer1=answer2=flag=0;
}
return 0;
}
2.广度优先搜素:
属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。它不同于深度优先的是,它是通过一个节点去拓展离它最近且满足条件的节点,逐层遍历,因此可以找到从初始状态到最终状态的路径。
基础模板:
void bfs(起始点) {
将起始点放入队列中;
标记起点访问;
while (如果队列不为空) {
访问队列中队首元素x;
删除队首元素;
for (x 所有相邻点) {
if (该点未被访问过且合法) {
将该点加入队列末尾;
}
}
}
队列为空,广搜结束;
}