BFS-DFS的综合运用+着色法+连通块


20200820写这一道题(01迷宫)花了整整一个下午,最后在写代码-翻书-思考-看题解-思考-写代码-翻书-思考-看题解-思考这样一个循环下,终于写了出来,激动之余,写篇博客记录下此时的心情以及AC的代码。

题目

题目描述

有一个仅由数字0与1组成的n×n格迷宫。若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上。

你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。

输入格式

第1行为两个正整数n,m。

下面n行,每行n个字符,字符只可能是0或者1,字符之间没有空格。

接下来m行,每行2个用空格分隔的正整数i,j,对应了迷宫中第i行第j列的一个格子,询问从这一格开始能移动到多少格。

输出格式

m行,对于每个询问输出相应答案。

输入输出样例

输入

2 2
01
10
1 1
2 2

输出

4
4

说明/提示

所有格子互相可达。

对于20%的数据,n≤10;

对于40%的数据,n≤50;

对于50%的数据,m≤5;

对于60%的数据,n≤100,m≤100;

对于100%的数据,n≤1000,m≤100000。

首先是BFS解法:

#include<stdio.h>
int n,m,startx,starty,ans;
int a[1005][1005],book[1005][1005];
int next[5][2]={
    
    {
    
    0,0},{
    
    0,1},{
    
    1,0},{
    
    0,-1},{
    
    -1,0}};
int head=1,tail=1;
struct note{
    
    
	int x; //横坐标
	int y; //纵坐标
};
struct note que[1000005];

void bfs(int x,int y)
{
    
    
	int tx,ty,ans=0;
	head=1,tail=1;  //初始化
	que[tail].x=x;  //初始坐标
	que[tail].y=y;
	book[x][y]=1;  //标记走过了
	ans++;    //审题,自身也算一个
	tail++;   
	while(head<tail){
    
     //当队列不为空时
		for(int i=1;i<=4;i++){
    
      //四个方向
			tx=que[head].x+next[i][0];
			ty=que[head].y+next[i][1];
			
			if(tx<1||tx>n||ty<1||ty>n) continue;  //是否越界
			
			//判断是否满足要求和是否以及走过
			if(a[que[head].x][que[head].y]!=a[tx][ty]&&book[tx][ty]==0){
    
    
				que[tail].x=tx;  //下一个坐标
				que[tail].y=ty;
				book[tx][ty]=1;  //标记
				ans++;  // 可以走,答案加1
				tail++;	
			}
			
		}
		head++;
	}
	for(int i=1;i<tail;i++){
    
    
		book[que[i].x][que[i].y]=ans;  // 同一个连通块的答案都一样,反反复复可以走 
	}
}

int main()
{
    
    
	scanf("%d%d",&n,&m); 
	for(int i=1;i<=n;i++){
    
    
		for(int j=1;j<=n;j++){
    
    
			scanf("%1d",&a[i][j]); //储存地图
		}
	}
	while(m--){
    
      //m个循环
		scanf("%d%d",&startx,&starty); //初始坐标
		if(book[startx][starty]) printf("%d\n",book[startx][starty]); //如果这个坐标标记过,即走过了,就直接输出
		else{
    
    
			bfs(startx,starty);//如果这个坐标没有走过,那么就搜索
			printf("%d\n",book[startx][starty]); //然后输出
		}
	}
	return 0;
} 

接下来是DFS解法

#include<stdio.h>
int n,m,ans[100002],sx,sy,book[1002][1002]; //ans用来存储答案,book用来标记
int a[1002][1002]; //a用来存储地图
int next[5][2]={
    
    {
    
    0,0},{
    
    0,1},{
    
    1,0},{
    
    0,-1},{
    
    -1,0}};//四个方向

void dfs(int x,int y,int z,int t){
    
    
	int tx,ty;
	book[x][y]=t;  //标记连通块
	ans[t]++;     //自身算一个,所以要加1
    for(int i=1;i<=4;i++){
    
    
    	tx=x+next[i][0]; //四个方向的坐标
    	ty=y+next[i][1];
    	if (tx<1 || ty>n || ty<1 || tx>n ) continue;  //放在越界
    	if(book[tx][ty]==0&&a[tx][ty]!=z){
    
      //判断是否已经走过和是否满足要求
    		dfs(tx,ty,!z,t); //!z可以改为a[tx][ty] 
    	}
    }
}

int main()
{
    
    
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
    
    
    	for(int j=1;j<=n;j++){
    
    
    		scanf("%1d",&a[i][j]);  //存储地图
    	}
    }
    for (int i=1;i<=m;i++)
    {
    
    
        scanf("%d%d",&sx,&sy);  //起始坐标
        if (book[sx][sy]==0) {
    
      //如果这个坐标还没有走过
        	dfs(sx,sy,a[sx][sy],i);  //则搜索遍历
        }
		else{
    
    
			ans[i]=ans[book[sx][sy]];	 //反之输出
		} 
    }
    for (int i=1;i<=m;i++)
    printf("%d\n",ans[i]);
    return 0;
}

这就是写了整整一个下午的题目,一开始思路都没有,接着有思路了,但是超时了三个,后来看提示说明才知道数据很大(这也给了我们一个启发,如果参加蓝桥杯、CCF等比赛时,不要过了样例马上就去提交哦,要看题目给的提示以及检查自己写的代码是否跟思路一致了),如果每输入一个起始坐标就重新搜一次肯定会超时。优化代码优化了一段时间还是超时三个,接着看题解慢慢得就发觉这是连通块+着色标记法,还好前一天有学习到,理解起来就没有那么困难!!!也有的同学是用并查集做的,但是本萌新还没有学到,就没有去了解。及时总结,复习,不然第二天还是会忘记的!!!挑战自己,让自己难起来,加油!!

猜你喜欢

转载自blog.csdn.net/qq_45735298/article/details/108129569