分别用BFS和DFS求给定的矩阵中“块”的个数

Posted CSU迦叶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分别用BFS和DFS求给定的矩阵中“块”的个数相关的知识,希望对你有一定的参考价值。

目录

背景介绍

BFS实现

基本思想

获取相邻位置元素技巧

BFS函数

DFS实现

基本思想

DFS函数

完整代码

背景介绍

背景

给出一个mxn的矩阵,矩阵中的元素为0或1。称位置(x,y)与其上下左右四个位置(x,y+1),(x,y-1),(x-1,y),(x+1,y)是相邻的。如果矩阵中有若干(可以是1)个1是相邻的(不必两两相邻),那么称这些1构成了一个“块”。求给定的矩阵中“块”的个数。

例子

0 1 1 1 0 0 1
0 0 1 0 0 0 0
0 0 0 0 1 0 0
0 0 0 1 1 1 0
1 1 1 0 1 0 0
1 1 1 1 0 0 0

上面给定的6x7的矩阵中块的个数是4。

BFS实现

基本思想

枚举每一个位置的元素,如果为0,则跳过;如果为1,则使用BFS查询与该位置相邻的4个位置(前提是不出界),判断它们是否为1(如果某个相邻的位置为1,则同样去查询与该位置相邻的4个位置,直到整个“1”块访问完毕)。而为了防止走回头路,一般可以设置一个bool型数组inq(即in queue的简写)来记录每个位置是否在BFS中已入过队(不是是否已被访问)

获取相邻位置元素技巧

增量数组+遍历

int X[] = {0,0,-1,1};
int Y[] = {1,-1,0,0};

for(int i=0;i<4;i++){
    newX = nowX + X[i];
    newY = nowY + Y[i];
}

BFS函数

//BFS的作用是访问(x,y)所在块中的所有结点
//具体访问操作是将inq[x][y]设置为1 
void BFS(int x,int y){
	queue<Node> que;
	node.x = x,node.y = y;//当前节点的坐标是(x,y)
	que.push(node);//头结点入队
	inq[x][y] = true;//设置已经入队
	while(!que.empty()){
		Node top = que.front();//取出队首元素
		que.pop();//队首元素出队
		for(int i=0;i<4;i++){
			int newX = top.x + X[i];
			int newY = top.y + Y[i];
			if(judge(newX,newY)){//如果该邻居节点可以入队 
				node.x = newX;
				node.y = newY;
				que.push(node);//入队
				inq[newX][newY] = true;//设置已经入队 
			}
		} 
	} 
} 

DFS实现

基本思想

DFS和BFS函数的作用相同。只不过不再需要队列,而是使用递归。关键步骤是,遇到符合要求的邻居结点,进一步看邻居的邻居是否符合要求。

DFS函数

//DFS的作用是访问(x,y)所在块中的所有节点
//具体访问操作是将inq[x][y]设置为1
void DFS(int x,int y){
	inq[x][y] = true;//设置(x,y)被访问过
	for(int i=0;i<4;i++){
		int newX = x + X[i];
		int newY = y + Y[i];
		if(judge(newX,newY)){//如果该邻居节点可以入队 
			inq[newX][newY] = true;
			DFS(newX,newY);//进一步搜索邻居的邻居 
		}
	} 
} 

完整代码

#include<cstdio>
#include<map>
#include<set>
#include<string>
#include<cstring>
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>

using namespace std;

const int maxn = 100;

struct Node{
	int x,y;//坐标(x,y) 
}node;

int n,m;//矩阵的大小是n*m
int matrix[maxn][maxn];//01矩阵
bool inq[maxn][maxn] =  {false};//初始化为全部没入过队列
int X[4] = {0,0,-1,1};//增量数组
int Y[4] = {1,-1,0,0};	 

bool judge(int x,int y){//判断位置(x,y)是否可以入队
	//越界,不入
	if(x>=n||x<0||y>=m||y<0)return false;
	//为0,不入 
	if(!matrix[x][y])return false;
	//已经入过,不入
	if(inq[x][y])return false;
	 
	return true;
}

//BFS的作用是访问(x,y)所在块中的所有结点
//具体访问操作是将inq[x][y]设置为1 
void BFS(int x,int y){
	queue<Node> que;
	node.x = x,node.y = y;//当前节点的坐标是(x,y)
	que.push(node);//头结点入队
	inq[x][y] = true;//设置已经入队
	while(!que.empty()){
		Node top = que.front();//取出队首元素
		que.pop();//队首元素出队
		for(int i=0;i<4;i++){
			int newX = top.x + X[i];
			int newY = top.y + Y[i];
			if(judge(newX,newY)){//如果该邻居节点可以入队 
				node.x = newX;
				node.y = newY;
				que.push(node);//入队
				inq[newX][newY] = true;//设置已经入队 
			}
		} 
	} 
}

//DFS的作用是访问(x,y)所在块中的所有节点
//具体访问操作是将inq[x][y]设置为1
void DFS(int x,int y){
	inq[x][y] = true;//设置(x,y)被访问过
	for(int i=0;i<4;i++){
		int newX = x + X[i];
		int newY = y + Y[i];
		if(judge(newX,newY)){//如果该邻居节点可以入队 
			inq[newX][newY] = true;
			DFS(newX,newY);//进一步搜索邻居的邻居 
		}
	} 
} 

int main(){
	
	scanf("%d%d",&n,&m);
	for(int x=0;x<n;x++){
		for(int y=0;y<m;y++){
			scanf("%d",&matrix[x][y]);
		}
	}
	
	int num = 0;//块数
	
	for(int x=0;x<n;x++){
		for(int y=0;y<m;y++){
			//如果元素为1且没如果队,那就是发现了一个新块 
			if(matrix[x][y]&&!inq[x][y]){
				num ++;
				DFS(x,y);
			} 
		}
	} 
	
	printf("%d\\n",num);	
	 
	return 0;
}

以上是关于分别用BFS和DFS求给定的矩阵中“块”的个数的主要内容,如果未能解决你的问题,请参考以下文章

UVa572 Oil Deposits (DFS求连通块)

油田(Oil Deposits)-用DFS求连通块

[FloodFill] aw1097. 池塘计数(bfs+dfs+FloodFill+模板题)

用DFS求连通块(种子填充)

用dfs求联通块(UVa572)

PAT甲题题解-1091. Acute Stroke (30)-BFS