搜索入门(DFS,BFS)笔记

1 篇文章 0 订阅
1 篇文章 0 订阅

BFS(广度优先搜索Breadth-First-Search)部分内容是百度的

广度优先算法(Breadth-First Search),同广度优先搜索,又称作宽度优先搜索,或横向优先搜索,简称BFS,是一种图形搜索演算法。简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点,如果发现目标,则演算终止。所谓广度,就是一层一层的,向下遍历,层层堵截。

所谓广度,就是一层一层的,向下遍历,层层堵截,还是这幅图,我们如果要是广度优先遍历的话,我们的结果是V1 V2 V3 V4 V5 V6 V7 V8。

1、访问顶点vi ;

2、访问vi 的所有未被访问的邻接点w1 ,w2 , …wk ;

3、依次从这些邻接点(在步骤②中访问的顶点)出发,访问它们的所有未被访问的邻接点; 依此类推,直到图中所有访问过的顶点的邻接点都被访问;

平面网格:

列如如下图,从1到9最少的步数是多少?(只允许上下左右这么走,很容易想到是4步)

BFS的步骤如下(图是按照从左到右,从上到下的顺序变化的,所以要一层一层的阅读):

代码如下:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<math.h>
using namespace std;
int s,e;
int sx,ex,sy,ey;
int mp[100][100];
int dy[4]={1,0,-1,0};
int dx[4]={0,1,0,-1};
struct node
{
	int x,y,step;
};
int check(int xx,int yy)
{
	if(xx>3||xx<1||yy>3||yy<1||mp[xx][yy]==1)
	    return 0;
	return 1;
}
int bfs()
{
	queue<node>qu;
	memset(mp,0,sizeof(mp));
	node now,nxt;
	now.x=sx,now.y=sy;
	mp[sx][sy]=1;
	now.step=0;
	qu.push(now);
	while(!qu.empty())
	{
		now=qu.front();
		qu.pop();
		if(now.x==ex&&now.y==ey)
		   return now.step;
		for(int i=0;i<4;i++)
		{
			nxt.x=now.x+dx[i];
			nxt.y=now.y+dy[i];
			if(check(nxt.x,nxt.y)==1)
		    {
		    	nxt.step=now.step+1;
		    	mp[nxt.x][nxt.y]=1;
		    	qu.push(nxt);
		    }
		}
	}
	return -1;
}
int main()
{
	while(scanf("%d%d",&s,&e)==2)
	{
		if(s==1) sx=1,sy=1;
		if(s==2) sx=2,sy=1;
		if(s==3) sx=3,sy=1;
		if(s==4) sx=1,sy=2;
		if(s==5) sx=2,sy=2;
		if(s==6) sx=3,sy=2;
		if(s==7) sx=1,sy=3;
		if(s==8) sx=2,sy=3;
		if(s==9) sx=3,sy=3;
		if(e==1) ex=1,ey=1;
		if(e==2) ex=2,ey=1;
		if(e==3) ex=3,ey=1;
		if(e==4) ex=1,ey=2;
		if(e==5) ex=2,ey=2;
		if(e==6) ex=3,ey=2;
		if(e==7) ex=1,ey=3;
		if(e==8) ex=2,ey=3;
		if(e==9) ex=3,ey=3;
		int ans=bfs();
		printf("%d\n",ans);
	}
	return 0;
}

/*

输入:
1 9
输出:
4

*/

空间复杂度

因为所有节点都必须被储存,因此BFS的空间复杂度为 O(|V| + |E|),其中 |V| 是节点的数目,而 |E| 是图中边的数目。注:另一种说法称BFS的空间复杂度为O(B^M),其中 B 是最大分支系数,而 M 是树的最长路径长度。由于对空间的大量需求,因此BFS并不适合解非常大的问题。

时间复杂度

最差情形下,BFS必须寻找所有到可能节点的所有路径,因此其时间复杂度为 O(|V| + |E|),其中 |V| 是节点的数目,而 |E| 是图中边的数目。

问题

F - Catch That Cow

这是到一维的BFS

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.

Sample Input

5 17

Sample Output

4

Hint

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.

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
using namespace std;
const int maxn=1e6+10;
int sx,ex;
int mp[maxn];
struct node
{
	int x,time;
};
int check(int xx)
{
	if(mp[xx]==1||xx<0||xx>100000)
	   return 0;
	return 1;
}
int bfs()
{
	memset(mp,0,sizeof(mp));
	queue<node>qu;
	node nxt,now;
	now.x=sx;
	mp[sx]=1;
	now.time=0;
	qu.push(now);
	while(!qu.empty())
	{
		now=qu.front();
		qu.pop();
		if(now.x==ex)
		   return now.time;
		for(int i=0;i<3;i++)
		{
			if(i==0)
			    nxt.x=now.x+1;
			else if(i==1)
			        nxt.x=now.x-1;
			     else nxt.x=2*now.x;
			if(check(nxt.x)==1)
			{
				nxt.time=now.time+1;
				mp[nxt.x]=1;
				qu.push(nxt);
			}
		}
	}
	return -1;
}
int main()
{
    scanf("%d",&sx);
    getchar();
    scanf("%d",&ex);
	int ans=bfs();
	printf("%d\n",ans);
}

G - Knight Moves

A friend of you is doing research on the Traveling Knight Problem (TKP) where you are to find the shortest closed tour of knight moves that visits each square of a given set of n squares on a chessboard exactly once. He thinks that the most difficult part of the problem is determining the smallest number of knight moves between two given squares and that, once you have accomplished this, finding the tour would be easy. 
Of course you know that it is vice versa. So you offer him to write a program that solves the "difficult" part. 

Your job is to write a program that takes two squares a and b as input and then determines the number of knight moves on a shortest route from a to b. 

Input

The input file will contain one or more test cases. Each test case consists of one line containing two squares separated by one space. A square is a string consisting of a letter (a-h) representing the column and a digit (1-8) representing the row on the chessboard. 

Output

For each test case, print one line saying "To get from xx to yy takes n knight moves.". 

Sample Input

e2 e4
a1 b2
b2 c3
a1 h8
a1 h7
h8 a1
b1 c3
f6 f6

Sample Output

To get from e2 to e4 takes 2 knight moves.
To get from a1 to b2 takes 4 knight moves.
To get from b2 to c3 takes 2 knight moves.
To get from a1 to h8 takes 6 knight moves.
To get from a1 to h7 takes 5 knight moves.
To get from h8 to a1 takes 6 knight moves.
To get from b1 to c3 takes 1 knight moves.
To get from f6 to f6 takes 0 knight moves.

我的思路:这道题可以用到上面类似的步骤一层一层递进,直至到达终点,也就得到最小步数了。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<math.h>
using namespace std;
char s[2],e[2];
long int sx,ex,sy,ey;
int mp[100][100];
int dx[8]={1,1,2,2,-1,-1,-2,-2};
int dy[8]={2,-2,1,-1,2,-2,1,-1};
struct node
{
	int x,y,step;
};
int check(int xx,int yy)
{
	if(xx>8||xx<1||yy>8||yy<1||mp[xx][yy]==1)
	    return 0;
	return 1;
}
int bfs()
{
	queue<node>qu;
	memset(mp,0,sizeof(mp));
	node now,nxt;
	now.x=sx,now.y=sy;
	mp[sx][sy]=1;
	now.step=0;
	qu.push(now);
	while(!qu.empty())
	{
		now=qu.front();
		qu.pop();
		if(now.x==ex&&now.y==ey)
		   return now.step;
		for(int i=0;i<8;i++)
		{
			nxt.x=now.x+dx[i];
			nxt.y=now.y+dy[i];
			if(check(nxt.x,nxt.y)==1)
		    {
		    	nxt.step=now.step+1;
		    	mp[nxt.x][nxt.y]=1;
		    	qu.push(nxt);
		    }
		}
	}
	return -1;
}
int main()
{
	while(scanf("%s",s)==1)
	{
		getchar();
		scanf("%s",e);
		sx=s[0]-'a'+1;
		sy=s[1]-'1'+1;
		ex=e[0]-'a'+1;
		ey=e[1]-'1'+1;
		int ans=bfs();
		printf("To get from %c%c to %c%c takes %d knight moves.\n",s[0],s[1],e[0],e[1],ans);
	}
	return 0;
}

DFS(深度优先搜索Depth-First-Search)部分内容是百度的

事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.

举例说明之:下图是一个无向图,如果我们从A点发起深度优先搜索(以下的访问次序并不是唯一的,第二个点既可以是B也可以是C,D),则我们可能得到如下的一个访问过程:A->B->E(没有路了!回溯到A)->C->F->H->G->D(没有路,最终回溯到A,A也没有未访问的相邻节点,本次搜索结束).

图

简要说明深度优先搜索的特点:每次深度优先搜索的结果必然是图的一个连通分量.深度优先搜索可以从多点发起.如果将每个节点在深度优先搜索过程中的"结束时间"排序(具体做法是创建一个list,然后在每个节点的相邻节点都已被访问的情况下,将该节点加入list结尾,然后逆转整个链表),则我们可以得到所谓的"拓扑排序",即topological sort. 

问题

B - Lake Counting

Due to recent rains, water has pooled in various places in Farmer John's field, which is represented by a rectangle of N x M (1 <= N <= 100; 1 <= M <= 100) squares. Each square contains either water ('W') or dry land ('.'). Farmer John would like to figure out how many ponds have formed in his field. A pond is a connected set of squares with water in them, where a square is considered adjacent to all eight of its neighbors. 
Given a diagram of Farmer John's field, determine how many ponds he has.

Input

* Line 1: Two space-separated integers: N and M 
* Lines 2..N+1: M characters per line representing one row of Farmer John's field. Each character is either 'W' or '.'. The characters do not have spaces between them.

Output

* Line 1: The number of ponds in Farmer John's field.

Sample Input

10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.

Sample Output

3

Hint

OUTPUT DETAILS: 
There are three ponds: one in the upper left, one in the lower left,and one along the right side.

我的思路:这道题的坑点在于要考虑八个方向。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<math.h>
using namespace std;
const int maxn=1e3+10;
typedef long long lli;
int n,m;
int dx[8]={1,-1,1,-1,0,0,1,-1};
int dy[8]={0,0,-1,1,1,-1,1,-1};
char mp[210][210];
void dfs(int y,int x)
{
	mp[y][x]='.';
	for(int i=0;i<8;i++)
   	{
   		int tx=x+dx[i];
  		int ty=y+dy[i];		
   		if(tx>m||tx<1||ty>n||ty<1||mp[ty][tx]=='.')
   		{
   			continue;
   		}
	    else dfs(ty,tx);
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		cin>>(mp[i]+1);
	}
    for(int i=1;i<=n;i++)
    {
    	for(int j=1;j<=m;j++)
     	{
    	   if(mp[i][j]=='W')
   		   {
    	    	ans++;
    	    	dfs(i,j);
    	   }
    	}
   	}
   	printf("%d",ans);
}

A - Red and Black

此题BFS和DFS两种方法我都尝试了一下

There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A man is standing on a black tile. From a tile, he can move to one of four adjacent tiles. But he can't move on red tiles, he can move only on black tiles. 

Write a program to count the number of black tiles which he can reach by repeating the moves described above. 

Input

The input consists of multiple data sets. A data set starts with a line containing two positive integers W and H; W and H are the numbers of tiles in the x- and y- directions, respectively. W and H are not more than 20. 

There are H more lines in the data set, each of which includes W characters. Each character represents the color of a tile as follows. 

'.' - a black tile 
'#' - a red tile 
'@' - a man on a black tile(appears exactly once in a data set) 

Output

For each data set, your program should output a line which contains the number of tiles he can reach from the initial tile (including itself). 

Sample Input

6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
11 6
..#..#..#..
..#..#..#..
..#..#..###
..#..#..#@.
..#..#..#..
..#..#..#..
7 7
..#.#..
..#.#..
###.###
...@...
###.###
..#.#..
..#.#..
0 0

Sample Output

45
59
6
13

我的思路:题目大意是让我们求出能够从@开始走动的最大范围,在这范围内最多走多少步,只有上下左右四个方向可以走

DFS代码:

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<math.h>
#include<algorithm>
using namespace std;
char mp[25][25];
int vis[25][25];
int sx,sy,step;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int w,h; 
int dfs(int sx,int sy)
{
	 mp[sy][sx]='#';
	 int sum=1;
	 for(int i=0;i<4;i++)
	 {
	 	int tx=sx+dx[i];
	 	int ty=sy+dy[i];
	 	if(tx>w||tx<1||ty>h||ty<1||mp[ty][tx]=='#')
	       continue;                               
        sum+=dfs(tx,ty);                 
	 }
	 return sum;
}
int main()
{
	while(scanf("%d%d",&w,&h)==2&&h&&w)
	{
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=h;i++)
		{
			cin>>(mp[i]+1);
		}
		for(int i=1;i<=h;i++)
		{
			for(int j=1;j<=w;j++)
			{
				if(mp[i][j]=='@')
				   sx=j,sy=i;
			}
		}
		printf("%d\n",dfs(sx,sy));
	}
} 

BFS代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<set>
#include<math.h>
#include<iostream>
using namespace std;
char mp[30][30];
int vis[30][30],sx,sy,sum;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int w,h;
struct node
{
	int x,y;
};
bool check(int xx,int yy)  //检查函数,用于检查下一步是否可以进行
{
	if(mp[yy][xx]=='#'||vis[yy][xx]==1||xx>w||xx<1||yy>h||yy<1)
	   return 0;
	return 1;
}
int bfs()
{
	sum=1;
	queue<node>qu;
	memset(vis,0,sizeof(vis));
	node now,nxt;
	now.x=sx;
	now.y=sy;
	qu.push(now);
	while(!qu.empty())
	{
		now=qu.front();
		qu.pop();
		for(int i=0;i<4;i++)
		{
			nxt.x=now.x+dx[i];
			nxt.y=now.y+dy[i];
			if(check(nxt.x,nxt.y))
			{
				vis[nxt.y][nxt.x]=1;
				sum++;
				qu.push(nxt);	
			}
		}
	}
	return sum;
}
int main()
{
	while(cin >> w >> h)
	{
		for(int i=1;i<=h;i++)
		{
			cin>>(mp[i]+1);
		}
		for(int i=1;i<=h;i++)
		{
			for(int j=1;j<=w;j++)
			{
			   if(mp[i][j]=='@')
			   {
			      sx=j,sy=i;
			      mp[i][j]='#';
			   }
			} 
		}
		if(w&&h)
	    {
		   int ans=bfs();
		   printf("%d\n",ans);
	    }
		else return 0;
	}
}

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值