24行代码AC_Oil Deposits UVA - 572(DFS解法+BFS解法+视频讲解)

Problem description

The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides
the land into numerous square plots. It then analyzes each plot separately, using sensing equipment to determine whether or not the plot contains oil.
A plot containing oil is called a pocket. If two pockets are adjacent, then they are part of the same oil deposit. Oil deposits can be quite large and may contain numerous pockets. Your job is to determine how many different oil deposits are contained in a grid.

Input

The input file contains one or more grids. Each grid begins with a line containing m and n, the number of rows and columns in the grid, separated by a single space. If m = 0 it signals the end of the input; otherwise 1 ≤ m ≤ 100 and 1 ≤ n ≤ 100. Following this are m lines of n characters each (not counting the end-of-line characters). Each character corresponds to one plot, and is either ‘*’, representing the absence of oil, or ‘@’, representing an oil pocket.

Output

For each grid, output the number of distinct oil deposits. Two different pockets are part of the same oil deposit if they are adjacent horizontally, vertically, or diagonally. An oil deposit will not contain more than 100 pockets.



分析与思考:

相较于标准的搜索模板题, 该题做了一些变形, 把求通路变形为求连通块的个数。 这类算法有一个很好听的名字: 种子填充

DFS解法:
从每个"@“格子除法,递归遍历它周围的”@"格子。每访问一个格子时,就给它写上一个“连通分量编号”(即下面的idx数组),这样就可以在访问之前检查它是否已经有了编号,从而避免同一个格子访问多次。

BFS解法:
思路与DFS解法类似,不过是把递归调用改为了队列调用。
使用二维数组对油田中每个点进行访问, 一旦遍历到@ ,且@没被访问过, 则将其入队进行BFS遍历,入队的所有@符号都设置为已被访问过。 遍历完成后sum+1。

总结:

  1. 由于DFS更易编写(BFS代码70行左右, DFS代码24行),因此常用DFS寻找连通块
  2. 迷宫问题的判定一般有三种:
    1. 判断该点是否越界
    2. 判断该点是否是障碍
    3. 判断该点是否已经走过
  3. 采用双重循环来找到当前格子的相邻8个格子,简洁高效(当然常量数组也可以)
  4. 对于先回溯后判定 还是 先判定后回溯要根据题意灵活选择, 一般来讲,先回溯,后判定效率更高。
  5. 最开始写BFS时,浩浩荡荡的搞了七八重嵌套,虽然也AC掉了,但看起来非常丑, 因此函数调用与功能分块还是很重要的。

DFS解法

#include<bits/stdc++.h>
#define Max 110
using namespace std;

char pic[Max][Max];
int m, n, idx[Max][Max];

void dfs(int r, int c, int id) {
    
    
	if(r<0 || r>=m || c<0 || c>=n) return;		//出界的格子
	if(idx[r][c] || pic[r][c]!='@') return;
	idx[r][c] =  id;
	for(int dr=-1; dr<=1; dr++) 		//八个方向的判定, 
		for(int dc=-1; dc<=1; dc++)
			if(dr!=0 || dc!=0) dfs(r+dr, c+dc, id);  
}

int main() {
    
    
	while(cin>>m>>n && m) {
    
    
		for(int i = 0; i < m; i++) cin>>pic[i];//二维char数组的输入
		memset(idx, 0, sizeof(idx));
		int cnt = 0;
		for(int i = 0; i < m; i++) 
			for(int j = 0; j < n; j++) 
				if(!idx[i][j] && pic[i][j]=='@') dfs(i, j, ++cnt);
		cout << cnt << '\n';
	}
return 0; }

BFS解法

#include<iostream>
#include<cmath>
#include<cstring> 
using namespace std;

struct Queue{
    
    
	int x, y;	//横纵坐标
	int s;		//步数 
}; 

int Next[4][2] = {
    
    1,0, -1,0, 0,1, 0,-1};
int vis[110][110];
char Oil_field[110][110];

int n, m;
int head, tail;

void Input() {
    
    
	for(int i = 0; i < n; i++) cin>>Oil_field[i];
}

void BFS(int i, int j) {
    
    
	//初始化 
	Queue que[10010];
	head = tail = 1;		//队列初始化 ,入第一个坐标
	//插入入口坐标
	que[tail].x = i, que[tail].y=j;
	que[tail].s = 0;
	tail++;
	vis[i][j] = 1;
	
	
	while(head < tail) {
    
    
		//枚举八个方向,这里仔细体会 
		for(int dr=-1; dr<=1; dr++) 
		for(int dc=-1; dc<=1; dc++) 
			if(dc!=0 || dr!=0) {
    
    
				//计算下一个点的坐标
		 		int tx = que[head].x + dr;
				int ty = que[head].y + dc;
				//1、判断是否越界
				if(tx<0 || tx>=n || ty<0 || ty>=m) continue;
				//2 3 判断是否是障碍物或已经在路径中
				if(Oil_field[tx][ty]=='@' && vis[tx][ty]==0) {
    
    
					//标记为已走过 
					//注意宽搜每个点只入队一次,所以不要需要连book数
					vis[tx][ty] = 1;
					que[tail].x = tx;
					que[tail].y = ty;
					que[tail].s = que[head].s + 1;	//步数+1
					tail++; 
				} 
			}	
	head++; 
	}
}

int main() {
    
    
	ios::sync_with_stdio(false); 
	while(cin>>n>>m && n) {
    
    
		memset(vis, 0, sizeof(vis));		//全局变量初始化 
		
		Input();
		
		int sum = 0;
		for(int i = 0; i < n; i++) 
			for(int j = 0; j < m; j++) {
    
    
				if(Oil_field[i][j]=='@' && !vis[i][j]) {
    
    	 
					BFS(i,j);
					sum++; 
				}
			}
		cout << sum << endl;
	}		//while循环的括号 
	return 0;
} 

这世界就是,一些人总在昼夜不停的努力,而另外一些人,起床就发现世界已经变了。 加油,陌生人!

猜你喜欢

转载自blog.csdn.net/weixin_43899069/article/details/108847403
今日推荐