基础算法整理-搜索
今天我们来看搜索。在信息学竞赛中,搜索是一类类似于模拟算法中的枚举策略的算法的统称。普通搜索最大的特点是效率低下,这也是每个OIer都应牢记于心的。
前置知识点(请读者先确保自己掌握以下算法):
- 基础语法(有关数组与函数)
- 递归
- 递推
- 模拟
- 栈和队列
常见的搜索主要分为BFS/DFS/A*(IDA*)几种,这些算法在NOIP所考察的基础算法中也占有重要地位。鉴于本人能力与见识有限,未能很好地掌握A*和IDA*,本篇博文将不会介绍这两种算法。
以下为正文(感觉这次没写好,希望同学们先看看其他Blog、做做题打下基础,再来看本文……)
第一个:DFS(Depth First Search,深度优先搜索)
深度优先搜索(下文简称“深搜”)是常用的几大搜索算法中最基础的一种,但也是变化最多(个人感觉),考察难度最难的一种。
基本思想:
- 起始状态
- 以起始状态为中心枚举所有可行的下一状态
- 每找到一个可行的下一状态便进入它,以该状态为中心执行 2 (这里就有递归)
- 当当前状态的所有下一状态全部枚举完时,保存结果(如果有的话)/ 判断是否找到通路(寻找路径问题)/ 取消标记(后面会讲到),并退回上一状态
这便是我所理解的深度优先搜索的基本框架:
DFS(当前状态){
遍历所有能走到的下一状态
如果该状态可以进入(条件因题而异,常见的为:未访问过的点,未考虑过的状态 等等) {(打上标记) 进入该状态[DFS(下一状态);]; (解除标记)}
}
//诡异的理解不是吗
然后是例题:
关于题目没什么好说的,从起点向终点搜的去,注意每次标记自己走过的道路以免绕圈(回退时取消标记!!!)……还有地图边界问题,每到达一次终点位置ans加1后跳回即可
代码:
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 int map[7][7]={0},m,n,fx,fy,ans=0; 5 void search(int x,int y) 6 { 7 8 9 if((x==fx)&&(y==fy)) 10 { 11 ans++; 12 } 13 else 14 { 15 if((x>n)||(map[x][y]==-1)||(y>m)||(x<=0)||(y<=0)) 16 { 17 map[x][y]=-1; 18 return; 19 } 20 else 21 { 22 //printf("POS(%d,%d)",y,x); 23 map[x][y]=-1; 24 search(x-1,y); 25 search(x+1,y); 26 search(x,y-1); 27 search(x,y+1); 28 } 29 } 30 map[x][y]=0; 31 return; 32 } 33 int main() 34 { 35 int t,sx,sy; 36 scanf("%d %d %d",&n,&m,&t); 37 /* 38 for(int i=1;i<=m;i++) 39 { 40 map[0][i]=map[n+1][i]=-1; 41 } 42 for(int i=1;i<=n;i++) 43 { 44 map[i][0]=map[m+1][i]=-1; 45 } 46 //Start up 47 */ 48 scanf("%d %d %d %d",&sx,&sy,&fx,&fy); 49 //Get start and Goal point 50 for(int i=1;i<=t;i++) 51 { 52 int mx,my; 53 scanf("%d %d",&mx,&my); 54 map[mx][my]=-1; 55 } 56 if(map[fx][fy]==-1) 57 { 58 printf("0"); 59 return 0; 60 } 61 //Input data: Obstacle:-1 62 search(sx,sy); 63 printf("%d",ans); 64 return 0; 65 }//一份略显稚嫩的代码(话说这是几年前写的?)
八皇后问题(这个就不给题号了,听过搜索的都知道……)
主要问题:如何确定当前斜向上有没有放过皇后,解决方法:用每个点的横纵坐标之差/和做标记表示斜向方向,差表示左上右下方向,和表示左下右上方向……然后就像其他搜索一样跑得去就可以了,代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 int n,num,ans[50]; 6 bool l[50]={0},sum[100]={0},del[200]={0}; 7 int map[15][15]; 8 void dfs(int k) 9 { 10 for(int i=1;i<=n;i++) 11 if((!l[i])&&(!sum[i+k])&&(!del[i-k+30])) 12 { 13 ans[k]=i; 14 if(k==n) 15 { 16 num++; 17 if(num<=3) 18 { 19 for(int j=1;j<=n;j++) 20 cout<<ans[j]<<" "; 21 cout<<endl; 22 } 23 } 24 else 25 { 26 l[i]=1;sum[i+k]=1;del[i-k+30]=1; 27 dfs(k+1); 28 l[i]=0;sum[i+k]=0;del[i-k+30]=0; 29 } 30 } 31 32 } 33 int main() 34 { 35 cin>>n; 36 dfs(1); 37 cout<<num; 38 return 0; 39 }//裸的一批
其他题目(自行练习):
P1101单词方阵 P3958(NOIP2017 D2T1)奶酪 其他自行上洛谷或其他网站搜寻
第二个:BFS(Breadth First Search,广(宽)度优先搜索)
如果说深搜是以栈(包括递归时调用的系统栈)为基础实现的算法,那么广搜便是以队列为基础的相似算法。
将所有可以到达的位置储存在一个队列中,每次从队首取出一个状态进行扩展。
与DFS最大的不同在于一般是用来处理最短路径的(因为通过广搜每次最先搜出的都是解决当前问题的最优路径,而深搜常用于判断是否能够到达目标状态)
例题:机器人搬重物
审题关键:最短。如果用深搜大概会超时(要把所有可能路径搜过一遍才能确定最短结果(虽然可以判time卡时间输出骗分这种鬼畜方法)),于是乎考虑广搜
代码:
1 #include<cmath> 2 #include<cstdio> 3 #include<iomanip> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 struct Node{ 8 int posx,posy,step,dir; 9 }; 10 struct NQue{ 11 private: 12 Node a[50000]; 13 int h,t,size; 14 public: 15 void Init(){h=t=size=0;} 16 void Ins(Node dt){a[h]=dt; h++; size++;} 17 Node Top(){return a[t];} 18 void Pop(){t++; size--;} 19 bool Empty(){return size==0;} 20 }q; 21 using namespace std; 22 int map[60][60],vis[60][60][5]; 23 int posx,posy,n,m,desx,desy; 24 char dir; 25 int main() 26 { 27 scanf("%d %d",&n,&m); 28 for(int i=1;i<=n;i++) 29 { 30 for(int j=1;j<=m;j++) 31 { 32 scanf("%d",&map[i][j]); 33 } 34 } 35 scanf("%d %d %d %d %c",&posx,&posy,&desx,&desy,&dir); 36 q.Init(); //Initialize the Queue 37 switch(dir){ //1 for East; 2 for South; 3 for West; 4 for North 38 case 'E' :{ 39 q.Ins((Node){posx,posy,0,1}); 40 break; 41 } 42 case 'S' :{ 43 q.Ins((Node){posx,posy,0,2}); 44 break; 45 } 46 case 'W' :{ 47 q.Ins((Node){posx,posy,0,3}); 48 break; 49 } 50 case 'N' :{ 51 q.Ins((Node){posx,posy,0,4}); 52 break; 53 } 54 default: {cerr<<"Input Err"<<endl; return 0;} 55 } 56 while(!q.Empty()) //BFS 57 { 58 Node Now=q.Top(); 59 q.Pop(); 60 if(Now.posx<=0||Now.posx>=n||Now.posy<=0||Now.posy>=m) continue; 61 if(map[Now.posx][Now.posy]||map[Now.posx+1][Now.posy]||map[Now.posx][Now.posy+1]||map[Now.posx+1][Now.posy+1]) continue; //Obstacle 62 if(vis[Now.posx][Now.posy][Now.dir]) continue; //if visited,jump 63 // cout<<setw(5)<<Now.posx<<setw(5)<<Now.posy<<" DIR="<<Now.dir<<" step:"<<Now.step<<endl; 64 if((Now.posx==desx)&&(Now.posy==desy)) {/*cout<<desx<<" "<<desy<<endl;*/ printf("%d",Now.step); return 0;} //Print the answer 65 switch(Now.dir){ 66 case 2:{ 67 q.Ins((Node){Now.posx,Now.posy,Now.step+1,1}); 68 q.Ins((Node){Now.posx,Now.posy,Now.step+1,3}); 69 q.Ins((Node){Now.posx+1,Now.posy,Now.step+1,2}); 70 q.Ins((Node){Now.posx+2,Now.posy,Now.step+1,2}); 71 if(map[Now.posx+2][Now.posy]||map[Now.posx+2][Now.posy+1]) break; 72 q.Ins((Node){Now.posx+3,Now.posy,Now.step+1,2}); 73 break; 74 } 75 case 1:{ 76 q.Ins((Node){Now.posx,Now.posy,Now.step+1,2}); 77 q.Ins((Node){Now.posx,Now.posy,Now.step+1,4}); 78 q.Ins((Node){Now.posx,Now.posy+1,Now.step+1,1}); 79 q.Ins((Node){Now.posx,Now.posy+2,Now.step+1,1}); 80 if(map[Now.posx][Now.posy+2]||map[Now.posx+1][Now.posy+2]) break; 81 q.Ins((Node){Now.posx,Now.posy+3,Now.step+1,1}); 82 break; 83 } 84 case 4:{ 85 q.Ins((Node){Now.posx,Now.posy,Now.step+1,1}); 86 q.Ins((Node){Now.posx,Now.posy,Now.step+1,3}); 87 q.Ins((Node){Now.posx-1,Now.posy,Now.step+1,4}); 88 q.Ins((Node){Now.posx-2,Now.posy,Now.step+1,4}); 89 if(map[Now.posx-1][Now.posy]||map[Now.posx-1][Now.posy+1]) break; 90 q.Ins((Node){Now.posx-3,Now.posy,Now.step+1,4}); 91 break; 92 } 93 case 3:{ 94 q.Ins((Node){Now.posx,Now.posy,Now.step+1,2}); 95 q.Ins((Node){Now.posx,Now.posy,Now.step+1,4}); 96 q.Ins((Node){Now.posx,Now.posy-1,Now.step+1,3}); 97 q.Ins((Node){Now.posx,Now.posy-2,Now.step+1,3}); 98 if(map[Now.posx][Now.posy-1]||map[Now.posx+1][Now.posy-1]) break; 99 q.Ins((Node){Now.posx,Now.posy-3,Now.step+1,3}); 100 break; 101 } 102 } 103 vis[Now.posx][Now.posy][Now.dir]=1; 104 } 105 cout<<-1; 106 return 0; 107 }
手写的队列,代码略长...
反过来涂色的经典例题……源码:
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 using namespace std; 5 int map[32][32]={0},n,statue; 6 void search(int x,int y) 7 { 8 //if((map[x][y]==-1)) 9 //{ 10 // statue=0; 11 // return; 12 //} 13 if((map[x][y]==1)||(map[x][y]==2)) 14 { 15 return; 16 } 17 else 18 { 19 map[x][y]=1; 20 if(x>0) search(x-1,y); 21 if(x<n)search(x+1,y); 22 if(y>1)search(x,y-1); 23 if(y<n)search(x,y+1); 24 map[x][y]=0; 25 } 26 //if(statue==1) 27 { 28 map[x][y]=2; 29 } 30 } 31 int main() 32 { 33 scanf("%d",&n); 34 for(int i=1;i<=n;i++) 35 { 36 for(int j=1;j<=n;j++) 37 { 38 scanf("%d",&map[i][j]); 39 } 40 } 41 for(int i=1;i<=n;i++) 42 { 43 map[0][i]=map[i][0]=map[i][n+1]=map[n+1][i]=-1; 44 } 45 for(int i=1;i<=n;i++) 46 { 47 if(map[0][i]==-1) search(0,i); 48 if(map[i][0]==-1) search(i,0); 49 if(map[n+1][i]==-1) search(n+1,i); 50 if(map[i][n+1]==-1) search(i,n+1); 51 } 52 for(int i=1;i<=n;i++) 53 { 54 for(int j=1;j<=n;j++) 55 { 56 printf("%d ",2-map[i][j]); 57 } 58 printf("\n"); 59 } 60 return 0; 61 }
关于搜索还有一些重要的知识(比如说剪枝[有毒]…),这里就不做介绍了(感觉自己写的也不太好,主要是没时间构思与准备),希望大家能从我的文章中得到分毫收获……
最后送给每位新手OIer一句话:Practice Makes Perfect! 加油练习吧!

浙公网安备 33010602011771号