DFS&BFS总结

DFS&BFS总结

目录..........................................................................................1

B - LakeCounting.............................1

C - Red and Black............................8

D - 胜利大逃亡................................14

A - 迷宫问题.....................................................................20

小结..........................................................................................27

基础知识不再介绍,直接看题目吧(*^^*)

POJ2386https://vjudge.net/problem/POJ-2386

题面:

Lake Counting

Time Limit: 1000MS

 

Memory Limit: 65536K

Total Submissions: 42011

 

Accepted: 20842

Description

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.

 

题目大意:

给你一个n*m的矩阵,每个点有两个状态,‘W’和‘.’。

如果一个W在以另一个W为中心的3*3矩阵中,则认为它们相邻。

* * *

*W*

* * *

在上图的八个位置的任一一个W都与中心的W相邻。

所有相邻的W构成一个连通块

目标是对于一个给定的矩阵,求连通块的数目,很好理解吧(#^.^#)

分析:

对于这类需要遍历所有状态的要求,一般使用DFS,毕竟递归函数更容易理解。所以我们可以遍历整个矩阵,每遇到一个‘W’就把它以及与它相邻的所有W改成‘.’,同时结果加1 。这样,每次我们遇到一个新的W,它一定不与之前找到的所有连通块相邻(如果相邻的话它就被改成‘.’了)。

代码:

首先遍历矩阵:

 

	for(int i = 0; i < n; i++)
	{
		for(int j = 0; j < m; j++)
		{
			judge(i, j);//对每一个块进行的操作 
		}
	}

 

对每一块进行的操作:

void judge(int i, int j)
{
	if(s[i][j] == 'W')
	{
		dfs(i, j);
		cnt++;
	}
}

dfs函数:

在本题中,需要遍历八个方块,可以用两个for循环枚举实现(当dx=dy=0时会遍历到自己,所以要在函数开始将s[x][y]改成‘.’)

void dfs(int x, int y)
{
	s[x][y] = '.';
	for(int dx = -1; dx <= 1; dx++)
	{
		for(int dy = -1; dy <= 1; dy++)
		{
			int x1 = x + dx;
			int y1 = y + dy;
			if(x1 < 0 || x1 >= n || y1 < 0 || y1 >= m) continue;
			if(s[x1][y1] == 'W')dfs(x1, y1);
		}
	}
}

AC代码:

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1e2 + 10;
char s[maxn][maxn];
int n, m;
int cnt = 0;
void dfs(int x, int y)
{
	s[x][y] = '.';
	for(int dx = -1; dx <= 1; dx++)
	{
		for(int dy = -1; dy <= 1; dy++)
		{
			int x1 = x + dx;
			int y1 = y + dy;
			if(x1 < 0 || x1 >= n || y1 < 0 || y1 >= m) continue;
			if(s[x1][y1] == 'W')dfs(x1, y1);
		}
	}
}
void judge(int i, int j)
{
	if(s[i][j] == 'W')
	{
		dfs(i, j);
		cnt++;
	}
}
void work()
{
	scanf("%d%d", &n, &m);
	for(int i = 0; i < n; i++) scanf("%s", s[i]);
	for(int i = 0; i < n; i++)
	{
		for(int j = 0; j < m; j++)
		{
			judge(i, j);//对每一个块进行的操作 
		}
	}
	printf("%d\n", cnt);
}
int main()
{
	work();
	return 0;
}

POJ1979(https://vjudge.net/problem/POJ-1979)

题面:

Red and Black

Time Limit: 1000MS

 

Memory Limit: 30000K

Total Submissions: 41475

 

Accepted: 22470

Description

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) 
The end of the input is indicated by a line consisting of two zeros. 

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

 

大意:

给你一个n*m的矩阵,每个点三种状态,‘.’或‘#’或‘@’。

一个人从@’出发,每次可以走上下左右四个方向,‘#’是墙不能走,问能走到多少个‘.’。

 

思路:

没啥好说的..直接对@所在点dfs一下就好了(* ̄︶ ̄)

 

 

 

dfs函数

int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};

/*这里只能走四个方向,直接把这四个方向的坐标变化记做常量数组就好*/


void dfs(int x, int y)
{
	if(vis[x][y]) return;
	vis[x][y] = true;
	cnt++;
	for(int i = 0; i < 4; i++)
	{
		for(int j = 0; j < 4; j++)
		{
			int x1 = x + dx[i];
			int y1 = y + dy[i];
			if(x1 < 0 || x1 >= n || y1 < 0 || y1 >= m) continue;
			if(s[x1][y1] == '.')dfs(x1, y1);
		}
	}
}

AC代码:

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 2e1 + 10;
char s[maxn][maxn];
int n, m;
bool vis[maxn][maxn];
int cnt = 0;
int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
void dfs(int x, int y)
{
	if(vis[x][y]) return;
	vis[x][y] = true;
	cnt++;
	for(int i = 0; i < 4; i++)
	{
		for(int j = 0; j < 4; j++)
		{
			int x1 = x + dx[i];
			int y1 = y + dy[i];
			if(x1 < 0 || x1 >= n || y1 < 0 || y1 >= m) continue;
			if(s[x1][y1] == '.')dfs(x1, y1);
		}
	}
}
void work()
{
	memset(s, 0, sizeof(s));
	memset(vis, 0, sizeof(vis));
	cnt = 0;
	for(int i = 0; i < n; i++) scanf("%s", s[i]);
	int x, y;
	for(int i = 0; i < n; i++)
	{
		for(int j = 0; j < m; j++)
		{
			if(s[i][j] == '@')
			{
				x = i;
				y = j;		
			}	
		} 
	}
	dfs(x, y);
	printf("%d\n", cnt);
}
int main()
{
	while(scanf("%d%d", &m, &n) == 2 && n + m)
	{
		work();
	} 
	return 0;
}

HDU1253https://vjudge.net/problem/HDU-1253

题面:

胜利大逃亡

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 42185    Accepted Submission(s): 14670

Problem Description

Ignatius被魔王抓走了,有一天魔王出差去了,这可是Ignatius逃亡的好机会.

魔王住在一个城堡里,城堡是一个A*B*C的立方体,可以被表示成AB*C的矩阵,刚开始Ignatius被关在(0,0,0)的位置,离开城堡的门在(A-1,B-1,C-1)的位置,现在知道魔王将在T分钟后回到城堡,Ignatius每分钟能从一个坐标走到相邻的六个坐标中的其中一个.现在给你城堡的地图,请你计算出Ignatius能否在魔王回来前离开城堡(只要走到出口就算离开城堡,如果走到出口的时候魔王刚好回来也算逃亡成功),如果可以请输出需要多少分钟才能离开,如果不能则输出-1.

 

 

 

 

Input

输入数据的第一行是一个正整数K,表明测试数据的数量.每组测试数据的第一行是四个正整数A,B,CT(1<=A,B,C<=50,1<=T<=1000),它们分别代表城堡的大小和魔王回来的时间.然后是A块输入数据(先是第0,然后是第1,2......),每块输入数据有B,每行有C个正整数,代表迷宫的布局,其中0代表路,1代表墙.(如果对输入描述不清楚,可以参考Sample Input中的迷宫描述,它表示的就是上图中的迷宫)

特别注意:本题的测试数据非常大,请使用scanf输入,我不能保证使用cin能不超时.在本OJ上请使用Visual C++提交.

 

 

Output

对于每组测试数据,如果Ignatius能够在魔王回来前离开城堡,那么请输出他最少需要多少分钟,否则输出-1.

 

 

Sample Input

1

3 3 4 20

0 1 1 1

0 0 1 1

0 1 1 1

1 1 1 1

1 0 0 1

0 1 1 1

0 0 0 0

0 1 1 0

0 1 1 0

 

 

Sample Output

11

 

题意:

给了一个a*b*c的魔方,每次有八个方向移动,问能否在时间t内从右下角到左上角。

这也是搜索类的题目,但与之前不同的是。前两道题我们都需要遍历所有的状态(或者说“点”)才能得出正确答案。但像这种求最短路或者最少操作次数的题目,只需要找出最短路径就可以了(即不必要遍历所有状态也可以找到最短路径,这里就不证明了,一是比较复杂,二来我也不会,O(_)O哈哈~)。所以我们要用BFS(宽度优先搜索),然后找到之后就退出搜索,这样可以节省时间。如果直接用DFS的话,一般都会T掉。

那么,怎么找到最短路呢,首先我们可以开一个三维数组d

d[x][y][z]表示从d[0][0][0]d[x][y][z]的距离,我们的目标就是求出从

d[0][0][0]d[a-1][b-1][c-1]的距离。

首先开三个全局数组

int p[maxn][maxn][maxn];//记录地图

int d[maxn][maxn][maxn];//记录距离

int dx[] = {1, -1, 0, 0, 0, 0}, dy[] = {0, 0, 1, -1, 0, 0}, dz[] = {0, 0, 0, 0, 1, -1};

我们先看看DFS怎么写:

 

void dfs(int x, int y, int z, int dis)
{
	for(int i = 0; i < 6; i++)
	{
		int x1 = x + dx[i];
		int y1 = y + dy[i];
		int z1 = z + dz[i];
		if(x1 < 0 || x1 >= a || y1 < 0 || y1 >= b || z1 < 0 || z1 >= c) continue;
		if(d[x1][y1][z1] > dis + 1 && p[x1][y1][z1] == 0)
		{
			d[x1][y1][z1] = dis + 1;
			dfs(x1, y1, z1, dis + 1);
		}
	}
}


和之前的差不多,就是把二维改成三维,没有什么本质差别。

怎么把它改成BFS呢?

DFSBFS的区别无非就是DFS优先遍历较远的状态,而BFS优先遍历附近的状态,BFS更适合于求最短路。

DFS呢,就好像从(0,0,0)走到(1,0,0),然后再走到(2,0,0)......总之就是一直往上走,走到不能走为止。然后在向(0,1,0)->(0,2,0)->(0,3,0)...一直往右走。边走边处理状态

BFS还是从(0,0,0)开始走,但是按定义,处理每个状态的顺序应该是(0 0 0) ->(0 1 0) ->(0 2 0) ->(0 1 1) ->(1 2 0)......

对于这种处理顺序,我们可以用队列来实现。

开始时处理(0,0,0)这个点。

对于它能到的三个点(1,0,0),(0,1,0),(0,0,1),我们虽然走到,但是先不处理,先将之加入队列中。现在(0,0,0)处理完了,将之从队列中删掉。

然后取出队首元素(1,0,0),它能到的三个点是(2,0,0)和(1,10)和(1,0,1)。还是先不处理,将之加入队列,但是新进入的元素处于队尾。也就是说,这时候队列中有四个元素,依次为:(0,1,0),(0,0,1),(2,0,0)和(1,10)。处理完(1,0,0)后队首的元素为(0,1,0)。这样,就达到了我们的目的,将所有状态按由近及远遍历(或者说按照找到这些状态的顺序来遍历,看你喜欢那种说法o((⊙﹏⊙))o)。

当然了,为了防止超时,对于同一个元素,我们仍然规定只能经过一次。可以证明,对于同一个状态,第二次到达时的距离不会优于第一次(基于同样的原因,这里不给出证明 ̄□ ̄||)。具体实现的时候,可以把每一个状态的距离初始化为一个非常大的常数INF。然后每次比较距离长短就可以了。

代码如下:

struct node
{
	int x, y, z;
	int dis;
};//把状态集成一个结构体
void bfs()
{
	queue<node>que;
	node k;
	k.x = 0;
	k.y = 0;
	k.z = 0;
	k.dis = 0;
	que.push(k);
	while(!que.empty())
	{
		k = que.front();
		que.pop();
		printf("(%d %d %d) ->", k.x, k.y, k.z);
		if(k.x == a - 1 && k.y == b - 1 && k.z == c - 1) return;
		node u;
		for(int i = 0; i < 6; i++)
		{
			u.x = k.x + dx[i];
			u.y = k.y + dy[i];
			u.z = k.z + dz[i];
			if(u.x < 0 || u.x >= a || u.y < 0 || u.y >= b || u.z < 0 || u.z >= c) continue;
			if(d[u.x][u.y][u.z] > k.dis + 1 && p[u.x][u.y][u.z] == 0)
			{
				d[u.x][u.y][u.z] = k.dis + 1;
				u.dis = k.dis + 1;
				que.push(u); 		
			}
		}
	}
}

POJ3984https://vjudge.net/problem/POJ-3984

题面:

迷宫问题

Time Limit: 1000MS

 

Memory Limit: 65536K

Total Submissions: 30475

 

Accepted: 17526

Description

定义一个二维数组: 


int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};


它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

Input

一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。

Output

左上角到右下角的最短路径,格式如样例所示。

Sample Input

0 1 0 0 0

0 1 0 1 0

0 0 0 0 0

0 1 1 1 0

0 0 0 1 0

Sample Output

(0, 0)

(1, 0)

(2, 0)

(2, 1)

(2, 2)

(2, 3)

(2, 4)

(3, 4)

(4, 4)

题意:中文题,还很短,就不说了吧0.0

思路:和上面那道题一样,不过由于数据量很小,用DFS也能过。

实际上,这道题的主要考点是输出路径,虽然也不难,但对于初学者来说,要将之实现也不是一件容易的事。

唔,只需要在遍历的时候对于每一个状态,记录一下它的前驱状态,然后回溯输出就可以了。

代码:

typedef struct U
{
	int x, y, num, t;
}a;//结构体,num代表前驱节点编号,为-1时表示无前驱。
dfs部分(偷个懒(ˉ▽ ̄~) ):
void dfs(int n) {
	if(u[n].x == 4 && u[n].y == 4) 
	{
		print(n);
		return; 
	}
	int k = 1;
	for(int i = 0; i < 4; i++) 
	{
		int xi = u[n].x + dx[i];
		int yi = u[n].y + dy[i];
		if(xi >= 0 && xi < 5 && yi >= 0 && yi < 5 && u[n].t + 1 < t[xi][yi] && s[xi][yi] == 0) 
		{
			while(u[n + k].num != -1) k++;
			u[n + k].x = xi;
			u[n + k].y = yi;
			u[n + k].num = n;
			u[n + k].t = u[n].t + 1;
			t[xi][yi] = u[n].t + 1;
			dfs(n + k);
			k++;
		}
	}
}

回溯输出部分:


void print(int i) {
	if(u[i].num != -1) 
	{
		print(u[i].num);
	}
	printf("(%d, %d)\n", u[i].x, u[i].y);
}

这样就可以保证从第一个点开始输出,一直输出到最后一个点啦!

AC代码:

#include<cstdio>
#include<cstring>
using namespace std;
const int INF = 10;
const int MAXT = 1000;
int s[INF][INF], t[INF][INF];
int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
typedef struct U
{
	int x, y, num, t;
}a;
a u[INF * INF];
void print(int i) {
	if(u[i].num != -1) 
	{
		print(u[i].num);
	}
	printf("(%d, %d)\n", u[i].x, u[i].y);
}
void dfs(int n) {
	if(u[n].x == 4 && u[n].y == 4) 
	{
		print(n);
		return; 
	}
	int k = 1;
	for(int i = 0; i < 4; i++) 
	{
		int xi = u[n].x + dx[i];
		int yi = u[n].y + dy[i];
		if(xi >= 0 && xi < 5 && yi >= 0 && yi < 5 && u[n].t + 1 < t[xi][yi] && s[xi][yi] == 0) 
		{
			while(u[n + k].num != -1) k++;
			u[n + k].x = xi;
			u[n + k].y = yi;
			u[n + k].num = n;
			u[n + k].t = u[n].t + 1;
			t[xi][yi] = u[n].t + 1;
			dfs(n + k);
			k++;
		}
	}
}
int main() {
    for(int i = 0; i < INF * INF; i++) 
	{
    	u[i].num = -1;
	}
	for(int i = 0; i < INF; i++) 
	{
		for(int j = 0; j < INF; j++) 
		{
			t[i][j] = MAXT;
		}
	}
	for(int i = 0; i < 5; i++) 
	{
		for(int j = 0; j < 5; j++) 
		{
			scanf("%d", &s[i][j]);
		}
	}
	dfs(0);
	return 0;
}

注:其实本题有个超级简单的写法,直接输出样例的输出就可以了0.0 。这道题只有一组测试数据,而且就是样例。。不知道你怎么想,反正我知道的时候感觉很Σ(⊙▽⊙"a...

 

 

 

 

 

 

 

 

 

 

小结:怎么说呢,DFSBFS确实是很基础的一些知识吧,特别是在图论中,经常会用到BFS来处理图。这两个难度也不高,尽量掌握吧╮(╯▽╰)..至少保证练习之后至少能独立码出一道题吧0.0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值