基础算法整理-搜索

 今天我们来看搜索。在信息学竞赛中,搜索是一类类似于模拟算法中的枚举策略的算法的统称。普通搜索最大的特点是效率低下,这也是每个OIer都应牢记于心的。

  前置知识点(请读者先确保自己掌握以下算法):

  1. 基础语法(有关数组与函数)
  2. 递归
  3. 递推
  4. 模拟
  5. 栈和队列

 常见的搜索主要分为BFS/DFS/A*(IDA*)几种,这些算法在NOIP所考察的基础算法中也占有重要地位。鉴于本人能力与见识有限,未能很好地掌握A*和IDA*,本篇博文将不会介绍这两种算法。

  以下为正文(感觉这次没写好,希望同学们先看看其他Blog、做做题打下基础,再来看本文……)

 

  第一个:DFS(Depth First Search,深度优先搜索)

 深度优先搜索(下文简称“深搜”)是常用的几大搜索算法中最基础的一种,但也是变化最多(个人感觉),考察难度最难的一种。

  基本思想:

  1.   起始状态
  2.        以起始状态为中心枚举所有可行的下一状态
  3.        每找到一个可行的下一状态便进入它,以该状态为中心执行 2 (这里就有递归)
  4.        当当前状态的所有下一状态全部枚举完时,保存结果(如果有的话)/ 判断是否找到通路(寻找路径问题)/ 取消标记(后面会讲到),并退回上一状态

 这便是我所理解的深度优先搜索的基本框架:

  DFS(当前状态){

    遍历所有能走到的下一状态

      如果该状态可以进入(条件因题而异,常见的为:未访问过的点,未考虑过的状态 等等) {(打上标记) 进入该状态[DFS(下一状态);]; (解除标记)}

  }

  //诡异的理解不是吗

  然后是例题:

  洛谷P1605 迷宫

  关于题目没什么好说的,从起点向终点搜的去,注意每次标记自己走过的道路以免绕圈(回退时取消标记!!!)……还有地图边界问题,每到达一次终点位置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! 加油练习吧!

posted @ 2018-10-25 00:21  氢离子活度指数  阅读(225)  评论(0)    收藏  举报