林大3.2训练【深度优先搜索】- 白与黑、迷宫寻路、猴群、01迷宫【已更新完成】

1、白与黑(林大OJ 784)

解决方案:直接深度优先搜索

Description

有一间长方形的房子,地上铺了白色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

Input

包括多个数据集合。每个数据集合的第一行是两个整数W 和H,分别表示x 方向
和y 方向瓷砖的数量。W 和H 都不超过20。在接下来的H 行中,每行包括W 个字符。
每个字符表示一块瓷砖的颜色,规则如下:
1)‘.’:黑色的瓷砖;
2)‘#’:白色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。
当在一行中读入的是两个零时,表示输入结束。

Output

对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。

Sample Input

6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
0 0

Sample Output

45

代码如下:

#include<bits/stdc++.h>

using namespace std;

int c,r;

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


const int N=20+5;

char Map[N][N];
int vis[N][N];
//.黑瓷砖
// #白瓷砖
//@ 目前位置 


void dfs(int i,int j)//i为行数,j为列数 
{
	Map[i][j]='#';
	ans++;
	for(int k=0;k<4;k++)
	{
		int x=i+dx[k];
		int y=j+dy[k];
		if(x>=1 && x<=r && y>=1 && y<=c && Map[x][y]=='.')
		{
			dfs(x,y);
		}

	}

}

int main()
{
	while(cin>>c>>r)
	{
		if(c==0 && r==0)break;
		
		int idx_i,idx_j;//记录初始位置
		for(int i=1;i<=r;i++)
			for(int j=1;j<=c;j++)
			{
				cin>>Map[i][j];
				if(Map[i][j]=='@')
				{
					idx_i=i;
					idx_j=j;
				}
			}
	
		ans=0;
	
		dfs(idx_i,idx_j);
		cout<<ans<<endl;
		
		memset(Map,0,sizeof(Map));
		
	}

						
	return 0;	
}

2、迷宫寻路(林大OJ 558)

Description

AC小公主很喜欢设计迷宫,她设计的迷宫只有两个口,一个入口,一个出口。但小公主有时候很调皮,她会让挑战者走不出迷宫。现在给你AC小公主的迷宫请你判断挑战者能否成功从出口走出迷宫,迷宫包证边界为障碍物,只有两个不同的入口。
“#”代表障碍物,“*”代表可走的路。

Input

输入有多组,每组输入两个正整数n,m( 2 < n < m < 1000)。
接下来n行,每行有m个字符。

Output

每组测试实例,若挑战者能走出迷宫输出”YES”,否则输出“NO”。

Sample Input

3 3
#*#
#*#
#*#
3 3
#*#
###
#*#

Sample Output

YES
NO

确定出入口开始搜索

代码如下:

#include<bits/stdc++.h>

using namespace std;

int n,m;


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

const int N=1000+5;

char a[N][N];

bool flag; 
void dfs(int x,int y)
{

	if( (x==n || y==m || x==1 || y==1) && a[x][y]=='*')
	{
		flag=true;
	}
	a[x][y]='#';
	for(int i=0;i<4;i++)
	{
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(nx>=1 && nx<=n && ny>=1 && ny<=m && a[nx][ny]=='*')
		{
			dfs(nx,ny);
		}
	}
}

int main()
{
	while(cin>>n>>m)
	{
		//随机挑选起点为sidx,sidy 
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				cin>>a[i][j];
			}
		
		for(int i=1;i<=m;i++)
		{
			if(a[n][i]=='*')
			{
				sidx=n;sidy=i;
			}
			if(a[1][i]=='*')
			{
				sidx=1;sidy=i;
			}
		}
		for(int j=1;j<=n;j++)
		{
			if(a[j][1]=='*')
			{
				sidx=j;sidy=1;
			}
			if(a[j][m]=='*')
			{
				sidx=j;sidy=m;
			}
		}
			
		a[sidx][sidy]='#';
		flag=false;
		
		dfs(sidx,sidy);
		
		if(flag)cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
		
	}	
}

3、猴群(林大OJ 1696)

Description

给你一个数字矩阵,其中数字由0-9构成,0代表树木,1-9代表猴子;凡是有0和边缘围起来的区域是猴群。请你计算猴群的数目?

Input

第1行为n和 m,代表行和列数,(1<=n,m<=100)

Output

输出猴群的数目!

Sample Input

4 10
0234500067
1034560500
2045600671
0000000089

Sample Output

4

遇到非零的,直接变成0,然后深度优先搜索周围全变成0,数量+1

代码如下:

#include<bits/stdc++.h>

using namespace std;

const int N=101;

char g[N][N];
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};

int cnt;

int n,m;//行列

//把当前坐标四周不是0的全部变成0 
void dfs(int x,int y)
{
	g[x][y]='0';
	for(int i=0;i<4;i++)
	{
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(nx>=1 && nx<=n && ny>=1 && ny<=m && g[nx][ny]!='0')
		{
			dfs(nx,ny);
		}
	}
}

int main()
{
	while(cin>>n>>m)
	{
		
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				cin>>g[i][j];
			}
			
		cnt=0;
		
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				if(g[i][j]!='0')
				{
					cnt++;
					dfs(i,j);
				}
			}	
		
		cout<<cnt<<endl;
		
	}	
	
	
	
	return 0;
} 

--------------------分割线-----------------

到目前为止一切正常,到下面位置第四题的时候迎接我的是无尽的TLE

在经过几次TLE后,终于发现这题不能用普通的深度优先搜索

以下是最终的solution

4、01迷宫(林大OJ 1702)

应该考虑用连通块来减少时间复杂度

Description

有一个仅由数字0与1组成的n×n格迷宫。若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上。
你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。

Input

第1行为两个正整数n,m.
下面n行,每行n个字符,字符只可能是0或者1,字符之间没有空格。
接下来m行,每行2个用空格分隔的正整数i,j,对应了迷宫中第i行第j列的一个格子,询问从这一格开始能移动到多少格。

Output

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

Sample Input

2 2
01
10
1 1
2 2

Sample Output

4
4

Hint

n<=1000,m<=100000

Source

洛谷

记忆化搜索+连通块解决问题

直接处理好图中每个点,明确他们属于哪个连通块,并且每个连通块的面积我们可以通过深度优先搜索求出来,存到ans里,接下来面对询问直接从ans输出答案

代码如下:

//我们发现每个连通块中能到达的点是一样的 
#include<bits/stdc++.h>

using namespace std;

const int N=1001;
const int M=1e6+10;

int n,m;

int q[M];
char g[N][N];
int vis[N][N];
int cnt=0,flag[N][N],ans[N*N];
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};

vector<int>r;

void dfs(int x,int y)
{
	vis[x][y]=1;
	flag[x][y]=cnt;//标记x,y这个点属于第cnt个连通块
	ans[flag[x][y]]++;//第cnt个连通块的面积+1 
	for(int i=0;i<4;i++)
	{
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(nx>=1 && nx<=n && ny>=1 && ny<=n && !vis[nx][ny] && g[nx][ny]!=g[x][y])
		{
			dfs(nx,ny);
		}
	}
}

int main()
{
	cin>>n>>m;
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>g[i][j];
	

	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			if(!vis[i][j])//如果没有访问过那就是其他的连通块 
			{
				cnt++;
				dfs(i,j);
			}
		}
		
	while(m--)
	{
		int o,p;
		cin>>o>>p;
		printf("%d\n",ans[flag[o][p]]);
	} 
	return 0;
}

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值