从一道题目体会BFS思想

题目描述:

给出一个m*n的矩阵,矩阵中的元素为0或1。称位置(x,y)与其上下左右四个位置是相邻的。如果矩阵中有若干个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
输出:4

思路

1.首先根据输入输出示例理解题意(很好理解)。
2.好,理解题意过之后,着重抓住位置(x,y)与其上下左右四个位置是相邻的这句话,这句话怎么理解呢,就是说(x,y)与(x,y+1),(x,y-1),(x+1,y),(x-1,y)是相邻的。“而如果矩阵中有若干个1相邻,则称这些块构成了一个块”,这也是解决本道问题的核心入手点。
3.而我们怎么判断某个1与周围的上下左右四个1相邻呢?我们不妨联系一下BFS的思想(Breadth First Search)["Breadth"即广度,为第一关键词,也就是说当碰到岔道口时,总是先依次访问从该岔道口能直接到达所有结点,然后再按这些结点被访问的顺序去依次访问它们能直接到达的所有结点,以此类推],由此不难联想到判断的手段是BFS。
4.当然在BFS中,为了防止走回头路,我们需要一个数组inq(即in queue的简写)来记录每个位置是否在BFS中已入过队。
5.另,此题枚举某个点的上下左右四个点时还用到了一个小技巧:设置两个增量数组来表示上下左右方向(很多题目都用到)。即:

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

这样就可以使用for循环来枚举4个方向,以确定与当前坐标(nowX,nowY)相邻的四个方向了,如下所示:

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

代码:

//BFS一般模板:
//1.定义队列q 并将起点s入队
//2.写一个while循环 循环条件是队列q非空
//3.在while循环中 先取出队首元素top 然后访问它(访问可以是任何事情,例如将其输出)
//4.将top的下一层结点中所有未曾入队的结点入队 并标记它们的层号为now的层号+1
//,同时设置这些入队的结点已入过队、

//本题代码如下:
#include<cstdio>
#include<queue>
using  namespace std;
const int maxn = 100;

struct node{
	int x,y;
}Node;

int n,m;
int matrix[maxn][maxn];
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是否需要访问) 
	//越界返回false
	if(x>=n||x<0||y>=m||y<0)return false;
	//当前位置为0,或(x,y)已过队 返回false
	if(matrix[x][y]==0||inq[x][y] == true)return false;
	//以上不满足 返回true
	return true; 
}

//BFS函数访问位置(x,y)所在的块 将该块中所有的"1"的inq都设置为true
void BFS(int x,int y){//结合BFS一般模板体会
	queue<node> Q;
	Node.x = x,Node.y = y;//当前结点的坐标为(x,y) 
	Q.push(Node);//将结点Node入队 
	inq[x][y] = true;//设置(x,y)已入队
	while(!Q.empty()){
		node top = Q.front();//取出队首元素 
		Q.pop();//队首元素出队 
		for(int i = 0;i<4;i++){
			int newX = top.x + X[i];
			int newY = top.y + Y[i];
			if(judge(newX,newY)){//如果新位置(newX,newY)需要访问 
				Node.x = newX,Node.y = newY;
				Q.push(Node);//将结点Node入队  
				inq[newX][newY] = true;//设置(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 ans = 0;//存放块数 
	for(int x = 0;x<n;x++){
		for(int y = 0;y<m;y++){
			//如果元素为1 且未入过队
			if(matrix[x][y] == 1 && inq[x][y] == false){
				ans++;
				BFS(x,y);//访问整个块 将该块中所有"1"的inq都标记为true 
			} 
		}
	}
	printf("%d\n",ans);
	return 0;
}

总结

1.对这个问题,求解的基本思想是:枚举每一个位置的元素,如果为0,则直接跳过;如果为1,则使用BFS查询与该位置相邻的4个位置(前提是不出界,编写一个judge()函数来判断),判断他们是否为1(如果某个相邻位置为1,则同样要去查询与该位置相邻的4个位置,直到整个“1”块访问完毕,这里停一下!仔细比较一下!和BFS最原始的思想是不是就是实际中的应用!!!)。
2.当然还有设置bool型数组inq以及设计增量数组两个小技巧,也需要体会,不过最重要的还应该是BFS的思想的领会

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值