[kuangbin带你飞] 专题一简单搜索

专题一——简单搜索

BFS和DFS

图的遍历

BFS可以理解为按层搜索,即从根节开始搜索,首先遍历距离根节点为1的节点接着遍历距离根节点为2.3...的节点,以此类推。

例如上图:bfs为1 2 5 3 4 6;

BFS目前我得理解是首先遍历根节点,利用队列先进先出的性质将与根节点邻接的节点依次入队,遍历完当前节点后将当前节点弹出队列,while循环当队不为空就一直遍历、入队、遍历、入队。

想像这个动态给过程就可以理解BFS了,以上图举例,假设1号节点是根节点,首先遍历1号点,发现2和5号点与1号点直接邻接 ,将2和5入队列,将1号点打印输出然后弹出队列,现在队列中只有2和5,接着顺序遍历队列中的2号点发现3和5号点和2邻接,而5号点已经在队列中了,只需要把三号点入队列接着打印输出2号点后弹出队列然后遍历5号点,4入队,5打印输出后出队,接着3号点,没有与之相邻的直接打印输出后出队,接着4号点,6号点入队,4号点打印输出后出队,然后6号点打印输出后出队。

所以BFS遍历为:1->2->5->3->4->6;

//bfs大致模板
int check() { }//剪枝函数
int bfs()
{
    queue<int> q;
    q.push(根节点);//将根节点入队
    while(!q.empty())
    {
        int p = q.front();//队列第一个节点
        q.pop();//弹出当前节点
        for(循环遍历与p节点相连的点)
        {
            if(check())//剪枝
                q.push(节点);
        }
        dosomething(p);//根据题意对当前遍历到的节点做一些操作,或输出或保存或计数或检验
    }
}

DFS可以理解为一条路走到黑,直到不能走了再往回退。

例如上图:dfs为1 5 4 6 3 2;

DFS我目前的理解是首先遍历根节点,然后利用栈先进后出的性质将与当前节点相邻接的节点入栈,遍历完当前节点后将节点弹出栈,while循环当栈不为空就一直遍历、入栈、遍历、入栈。。。

对比BFS再来想像DFS的动态给过程就可以很好理解DFS了,以上图举例,假设1号节点是根节点,首先遍历1号点,发现2和5号点与1号点直接邻接 ,将2和5入栈,将1号点打印输出然后弹出栈,现在栈中只有2和5,接着顺序遍历栈顶元素5号点发现4和2号点和5邻接,而2号点已经在栈中了,只需要把4号点入栈接着打印输出5号点后弹出队列,然后遍历栈顶4号点,3和6入栈,4打印输出后出栈,接着6号点,没有与之相邻的直接打印输出后出栈,接着3号点,3号点打印输出后出栈,然后2号点打印输出后出栈。

所以DFS遍历为:1->5->4->6->3->2;

//dfs大致模板
//利用栈来完成
int check() { }//剪枝函数
int dfs()
{
    strack<int> s;
    q.push(根节点);//将根节点入栈
    while(!s.empty())
    {
        int p = s.top();//栈中第一个节点
        s.pop();//弹出当前节点
        for(循环遍历与p节点相连的点)
        {
            if(check())//剪枝
                s.push(节点);
        }
        dosomething(p);//根据题意对当前遍历到的节点做一些操作,或输出或保存或计数或检验
    }
}

//利用递归
int check() { }//剪枝函数
int dfs(int i)
{
    if(遍历完成)
        return ;//计数或者保存或者输出
    for(int i;遍历当前节点的所有未遍历的相邻节点)
    {
        if(chaeck())
        {
            vis[i]=1;//打标记,表明走过i号节点
            dfs(i);//遍历i的邻接节点
            vis[i]=0//遍历完i后一定要清除标记
        }
    }
}

DFS和BFS的区别

  1. dfs更适合于求全部的解或者全部的解的个数;
  2. bfs更适合求最优解;
  • dfs比较耗时间,因为每一种解都要走到头才能求下一个解,所以适合求全部的解或全部解的个数。
  • 而bfs是按层搜索,当遍历某一层时如果遇到符合条件的解就可以立刻结束搜索,方便求取距离根节点最近的解(最优解),但是bfs的每个节点都要逐一保存到队列中,比较耗费空间。

Catch That Cow

Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,000) on a number line and the cow is at a point K (0 ≤ K ≤ 100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting.

* Walking: FJ can move from any point X to the points - 1 or + 1 in a single minute
* Teleporting: FJ can move from any point X to the point 2 × X in a single minute.

If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it?

INPUT

  • Line 1: Two space-separated integers: N and K

OUTPUT

  • Line 1: The least amount of time, in minutes, it takes for Farmer John to catch the fugitive cow.

测试样例

5 17

样例输出

4

提示

The fastest way for Farmer John to reach the fugitive cow is to move along the following path: 5-10-9-18-17, which takes 4 minutes.

题意理解:

  • 有一个农夫和一头牛,农夫在N点牛在P点,牛不动,农夫想抓住那头牛。
  • 农夫有两种移动方式,一种是walking,可以花费一分钟向前走一步或者向后走一步;另一种是teleporting,可以花费一分钟向前传送2*N的距离。
  • 注意:这里的传送不是指每次走N米远,而是如果现在是N=5就可以走到N=10,再次传送就可以从N=10走到N=20。而不是5,10,15这样走,也不是每次走2*N远,而是从N走到2N。
  • 求抓住牛的最短时间。

解题思路:

透撤的理解完题意就可以开始做题了。

这道题不是很好想出来bfs的思路,首先,题目要求的是最优解,首先就我目前所学唯一想到的是bfs和dp还有贪心。看了一下测试数据和提示,在每一步都可能后退,基本就排除贪心了,想了想dp后来觉得和dfs差不多而且还是dfs来的直接。

农夫在任意一点都有可能前进一步、后退一步、或者传送。相当于是一个三叉树,树的每个节点的深度是每个节点要花费的时间,所以按层搜索第一个遇到的解就是最优解了,很好的体现出了BFS的性质。

例如测试样例:

5 17

这就是这道题的思路了,代码贴在下面:

代码:

#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
const int N = 2000000;
int vis[N+10];//每个点的访问情况 
int n,k;
struct node
{
    int x,step;
};
int check(int x)
{
    if(x<0 || x>=N || vis[x])
        return 0;
    return 1;
}
int bfs(int x)
{
	queue<node>Q;
	node p,next;
	//将当前点加入判断的情况 
	p.step=0,vis[x]=1,Q.push(p);
	while(!Q.empty())
	{
		p=Q.front();
		Q.pop();
		if(p.x==k)
			return p.step; 
		//分三种情况把点加入队列等待后来的一一遍历 
		next.x=p.x+1;
		if(check(next.x))
		{
			next.step=p.step+1;
			vis[next.x]=1;
			Q.push(next);
		}
		next.x=p.x-1;
		if(check(next.x))
		{
			next.step=p.step+1;
			vis[next.x]=1;
			Q.push(next);
		}
		next.x=p.x*2;
		if(check(next.x))
		{
			next.step=p.step+1;
			vis[next.x]=1;
			Q.push(next);
		} 
	} 
	return 0;
} 
int main()
{
	int ans;
    while(~scanf("%d%d",&n,&k))
    {
        memset(vis,0,sizeof(vis));
        ans = bfs(n);
        printf("%d\n",ans);
    }
    return 0;
	return 0;
}

再放一道DFS↓↓↓

棋盘问题

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

INPUT

  • 输入含有多组测试数据。 
  • 每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n 
  • 当为-1 -1时表示输入结束。 
  • 随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。 

OUTPUT

  • 对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。

测试样例

2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1

样例输出

2
1

题意理解:

  • 给定的大小棋盘,棋盘形状是不规则的,其中‘#’表示的区域是棋盘区域,‘.’点代表的区域是棋盘外区域。
  • 棋盘的同一行同一列只能摆放一枚棋子。
  • 给定摆放棋子数,求所有可行的摆放方案数。

解题思路:

这道题要求全部解的数量,初步判定,运用DFS深度优先搜索来解。

首先逐行遍历找可以放棋子的位置,找到一个就用一个一维数组标记这一列,然后行数加一继续按行找可以放棋子的位置,找到后用一维数组标记这一列,一直重复如上操作直到棋子全部摆放完成,且遍历没有超出行数和列数的限制,这种方案成,方案数加一。显然这是在第上一个棋子确定位置后下一个棋子递归来确定位置,而上一个棋子确定位置的方式是通过循环来解,所以可以想到这道题DFS函数的形式大概是一个for循环加剪枝里面是递归。当棋子摆放完则方案成,return。

最后再计数就好了。

代码如下:

代码:

#include<cstdio>
#include<cstring>
int b[10],n,k,C;
char a[10][10];
void dfs(int x,int num)
{
    if(k==num) //判断该放置的棋子数是否摆完
    {  
        C++;//方案成
        return;
    }
    if(n<x) return;//x大于行数n
    for(int m=0;m<n;m++)
        if(!b[m]&&a[x][m]=='#')//m列未摆放且x行m列为可摆放处
        {
            b[m]=1;//标记m列已经摆放棋子
            dfs(x+1,num+1);
            b[m]=0;//清除标记
        }
    dfs(x+1,num);//x行不可摆放进行下一行位置摆放判断
}
int main()
{
    int i;
    while(scanf("%d%d",&n,&k)&&(n!=-1)&&(k!=-1))
    {
        for(i=0;i<n;i++)  
               scanf("%s",a[i]);
        memset(b,0,sizeof(b));
        C=0;//摆放方案
        dfs(0,0);
        printf("%d\n",C);
    }
    return 0;
}

B - Dungeon Master

You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is composed of unit cubes which may or may not be filled with rock. It takes one minute to move one unit north, south, east, west, up or down. You cannot move diagonally and the maze is surrounded by solid rock on all sides. 

Is an escape possible? If yes, how long will it take? 

INPUT

  • The input consists of a number of dungeons. Each dungeon description starts with a line containing three integers L,
  • R and C (all limited to 30 in size). 
  • L is the number of levels making up the dungeon. 
  • R and C are the number of rows and columns making up the plan of each level. 
  • Then there will follow L blocks of R lines each containing C characters. Each character describes one cell of the dungeon. A cell full of rock is indicated by a '#' and empty cells are represented by a '.'. Your starting position is indicated by 'S' and the exit by the letter 'E'. There's a single blank line after each level. Input is terminated by three zeroes for L, R and C.

OUTPUT

 Each maze generates one line of output. If it is possible to reach the exit, print a line of the form

Escaped in x minute(s).


 where x is replaced by the shortest time it takes to escape. 
If it is not possible to escape, print the line  

Trapped!  

测试样例

3 4 5
S....
.###.
.##..
###.#

#####
#####
##.##
##...

#####
#####
#.###
####E

1 3 3
S##
#E#
###

0 0 0

样例输出

Escaped in 11 minute(s).
Trapped!

题意理解:

  • 这道题是一个3d迷宫,起点是S终点是E,输入一个迷宫输出能不能通过,能通过用了几分钟,每走一步是一分钟。
  • 输入n组数据最后以000结束
  • 开始三个数代表长宽高
  • 后面n个矩阵代表n层
  • 其中每个矩阵"."点代表可以走"#"代表是墙壁不能通过。

解题思路:

这是一道很存粹的bfs题,求通过花费几分钟或不能通过。不用求有几条路,所以明显是一个最优解问题只不过这是一个三维的搜索,二维搜索是有上下左右四个方向,三维就有上下左右前后六个方向,相当于是一个很纯粹的搜索遍历,只不过是一个六叉树,通过一些剪枝应该会排除不少情况,比如走的时候不能大于矩阵的边界或者小于零,不能走墙壁。按照最开始那个bfs大概的套路应该很容易能写出代码。其中,找邻接节点那一步有一个小技巧,因为有六个方向,一一列举会增加代码长度,写起来也会浪费很多时间,其实可以用一个循环来列举所有情况。

伪代码如下:

int dx[6] = {-1,1,0,0,0,0};
int dy[6] = {0,0,-1,1,0,0};
int dz[6] = {0,0,0,0,-1,1};
for(int i=0;i<6;i++)
{
    point.x += dx[i];
    point.y += dy[i];
    point.z += dz[i];
}

 

代码如下:

代码:

 

#include <iostream>
#include <string>
#include <cstring>
#include <queue>
using namespace std;
 
struct Node{
    int x,y,z;
    int cont;
};
char Map[35][35][35];
int visit[35][35][35];
int dir[6][3] = {{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}};
int l,r,c;
int x1,y1,z1;
 
void bfs(){
    queue <Node> Q;
    Node t;
    t.x = x1,t.y = y1,t.z = z1,t.cont = 0;
    visit[x1][y1][z1] = 1;
    Q.push(t);
    while(!Q.empty()){
        Node res = Q.front();
        Q.pop();
 
        int xx = res.x;
        int yy = res.y;
        int zz = res.z;
        int Cont = res.cont;
 
        if(Map[xx][yy][zz] == 'E'){     //目标位置且一定最短
            cout<<"Escaped in "<<Cont<<" minute(s)."<<endl;
            return ;
        }
 
        for(int i = 0;i < 6;i ++){      //每一步6个状态
            Node temp;
            int xi = temp.x = xx+dir[i][0];
            int yi = temp.y = yy+dir[i][1];
            int zi = temp.z = zz+dir[i][2];
            temp.cont = Cont+1;
            if(xi < 1 || xi > l || yi < 1 || yi > r || zi < 1 || zi > c)    continue;       //边界判断
            if(Map[xi][yi][zi] != '#' && !visit[xi][yi][zi]){
                visit[xi][yi][zi] = 1;
                Q.push(temp);
            }
        }
    }
    cout<<"Trapped!"<<endl;
}
int main()
{
    while(cin>>l>>r>>c){
        if(l == 0 && r == 0 && c == 0)  break;
        for(int i = 1;i <= l;i ++){
            for(int j = 1;j <= r;j ++){
                for(int k = 1;k <= c;k ++){
                    cin>>Map[i][j][k];
                    if(Map[i][j][k] == 'S')
                        x1 = i,y1 = j,z1 = k;
                }
            }
        }
        memset(visit,0,sizeof(visit));
        bfs();
    }
    return 0;
}

 

如有错误及时联系更正。。。


## 3月24日 更新:棋盘问题;

## 3月25日 更新:catch that cow;Dungeon Master;

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值