dfs算法题目(深搜思想训练)

迷宫游戏
我们用一个二维的字符数组来表示前面画出的迷宫:
在这里插入图片描述
其中字符S表示起点,字符T表示终点,字符*表示墙壁,字符.表示平地。你需要从S出发走到T,每次只能向上下左右相邻的位置移动,不能走出地图,也不能穿过墙壁,每个点只能通过一次。你需要编程来求解一种从起点到终点的走法。
在这里插入图片描述
C语言代码:

#include<iostream> 
#include<string>
using namespace std;
int m,n;
bool vis[110][110];
string maze[110];
bool in(int x,int y){//判断当前点在不在地图内 
	return x>=0&&x<n&&y>=0&&y<m;
}
bool dfs(int x,int y){//从x,y点往下搜索 
	if(maze[x][y]=='T'){//找到了终点返回true 
		return true;
	}
	vis[x][y]=1;//标记此点被找过了 
	maze[x][y]='m';//标记寻找路线 
	int tx=x-1,ty=y;//向上搜索 
	if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){//看是否满足条件 
		if(dfs(tx,ty)){//满足条件返回true 
			return true;
		}
	}
	tx=x;ty=y-1;//向左搜索 
	if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
		if(dfs(tx,ty))
		return true;
	}
	tx=x+1,ty=y;//向下搜索 
	if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
		if(dfs(tx,ty)){
			return true;
		}
	}
	tx=x,ty=y+1;//向右搜索 
	if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
		if(dfs(tx,ty)){
			return true; 
		}
	}
	vis[x][y]=0;
	maze[x][y]='.';//做到不满足条件的地方回溯,取消此点标记 
	return false; 
	
}
int main(){
	cin>>n>>m;
	for(int i=0;i<n;i++){//输入地图  
		cin>>maze[i]; 
	}
	int x,y;
	for(int i=0;i<n;i++){//找到起点 
		for(int j=0;j<m;j++){
			if(maze[i][j]=='S'){
				x=i,y=j;
			}
		}
	}
	if(dfs(x,y)){//从起点开始搜索 
		for(int i=0;i<n;i++)
		cout<<maze[i]<<endl;//打印地图 
	}else{
		cout<<"NO!"<<endl;
	}
	return 0;
}

运行结果:
在这里插入图片描述
我们会发现,代码比较长,而且四个方向的代码其实一样,这里我们就可以通过一个数组来进行优化,表示四个方向
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
这样来选择每个方向:

	for(int i=0;i<4;i++){
		int tx=x+dir[i][0];
		int ty=y+dir[i][1];
		if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
			if(dfs(tx,ty)){
				return true;
			}
		}
	}

优化后的代码:

#include<iostream> 
#include<string>
using namespace std;
int m,n;
bool vis[110][110];
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
string maze[110];
bool in(int x,int y){//判断当前点在不在地图内 
	return x>=0&&x<n&&y>=0&&y<m;
}
bool dfs(int x,int y)
{
	if(maze[x][y]=='T'){
		return true;
	}
	vis[x][y]=1;
	maze[x][y]='m';
	for(int i=0;i<4;i++){
		int tx=x+dir[i][0];
		int ty=y+dir[i][1];
		if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
			if(dfs(tx,ty)){
				return true;
			}
		}
	}
	vis[x][y]=0;
	maze[x][y]='.';
	return false;
} 
int main(){
	cin>>n>>m;
	for(int i=0;i<n;i++){//输入地图  
		cin>>maze[i]; 
	}
	int x,y;
	for(int i=0;i<n;i++){//找到起点 
		for(int j=0;j<m;j++){
			if(maze[i][j]=='S'){
				x=i,y=j;
			}
		}
	}
	cout<<endl;
	if(dfs(x,y)){//从起点开始搜索 
		for(int i=0;i<n;i++)
		cout<<maze[i]<<endl;//打印地图 
	}else{
		cout<<"NO!"<<endl;
	}
	return 0;
}

运行结果:
在这里插入图片描述
中国象棋:
中国象棋博大精深,其中马的规则最为复杂,也是最难操控的一颗棋子。
我们都知道象棋中马走日,并且存在撇马脚的,现在要求输入m行n列棋局,S表示马的起点,T 表示马的终点,#表示这里有一颗棋子,.表示空地,现在让你模仿马的行走,判断是否可以走到终点,可以的化输出行走路径,不行的话输出NO;
C语言代码:


#include<string> 
#include<iostream>
using namespace std;
int m,n;
string map[110];
bool vis[110][110];//记录是否走过;
int dir[8][2]={{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}} ;
int dir_[8][2]={{1,0},{0,1},{0,1},{-1,0},{-1,0},{0,-1},{0,-1},{1,0}};//蹩马腿 
bool in(int x,int y){
	return x<m&&x>=0&&y>=0&&y<n;
}
bool dfs(int x,int y){
	if(map[x][y]=='T'){
		return true;
	}
	vis[x][y]=true;
	map[x][y]='m';
	for(int i=0;i<8;i++){
		int tx=x+dir[i][0];
		int ty=y+dir[i][1];
		if(in(tx,ty)&&map[tx][ty]!='#'&&!vis[tx][ty]&&map[x+dir_[i][0]][y+dir_[i][1]]=='.'){//最后一个是判断是否蹩马腿 
			if(dfs(tx,ty)){
				return true;
			}
		}
	} 
	vis[x][y]=false;
	map[x][y]='.';
	return false; 
} 
int main()
{ 
    int x,y;//m行n列 
	cin>>m>>n;
	for(int i=0;i<m;i++){
		cin>>map[i];
	}
	for(int i=0;i<m;i++){//寻找起点 
		for(int j=0;j<n;j++){
			if(map[i][j]=='S'){
				x=i;
				y=j;
			}
		}
	}
	cout<<endl;
	if(dfs(x,y)){
		for(int i=0;i<m;i++){
		   cout<<map[i]<<endl;
		}
	}else{
	     cout<<"NO!";
	} 
	return 0;
} 

这种只需要找到的深搜思想我们可以理解为:一个军营派出很多士兵去寻找敌人,有的士兵半路就牺牲了(这一部分是不符合条件的)就会return false告诉上面的士兵这条路走不通,一旦有士兵找到,就return true给上面发信号,上面收到信号第一时间停止所有士兵的寻找。并且报告给主函数找到了。

接下来我们处理边界的情况,假设马一定可以到达终点,但是有很多种走法,我们需要输出花费时间最短的步数怎么办?
我们可以定义一个全局变量来记录一个最小值,初始的时候,ans要赋值成为一个很大的数。并且dfs需要一个参数step来记录步数:


#include<string> 
#include<iostream>
using namespace std;
int m,n;
string map[110];
bool vis[110][110];//记录是否走过;
int ans=100000000;
int dir[8][2]={{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}} ;
int dir_[8][2]={{1,0},{0,1},{0,1},{-1,0},{-1,0},{0,-1},{0,-1},{1,0}};//蹩马腿 
bool in(int x,int y){
	return x<m&&x>=0&&y>=0&&y<n;
}
void dfs(int x,int y,int step){
	if(map[x][y]=='T'){
		if(step<ans){
			ans=step;
		}
		return;
	}
	vis[x][y]=true;
	for(int i=0;i<8;i++){
		int tx=x+dir[i][0];
		int ty=y+dir[i][1];
		if(in(tx,ty)&&map[tx][ty]!='#'&&!vis[tx][ty]&&map[x+dir_[i][0]][y+dir_[i][1]]=='.'){//最后一个是判断是否蹩马腿 
			dfs(tx,ty,step+1);
		}
	} 
	vis[x][y]=false;
} 
int main()
{ 
    int x,y;//m行n列 
	cin>>m>>n;
	for(int i=0;i<m;i++){
		cin>>map[i];
	}
	for(int i=0;i<m;i++){//寻找起点 
		for(int j=0;j<n;j++){
			if(map[i][j]=='S'){
				x=i;
				y=j;
			}
		}
	}
    dfs(x,y,0);
    cout<<ans<<endl;
} 

这种深搜思想我们可以理解为:一个军营派出很多士兵去寻找敌人,有的士兵半路就牺牲了(这一部分是不符合条件的),有的士兵找到了敌人就报上了自己花了多少步,然后发出return命令让他休息(要不然他会接着找),长官将步数进行对比并且比出最小的步数,到所有的士兵牺牲的牺牲休息的休息,那寻找也就结束了。

象棋土木改编,输入两数m,n代表网格的大小,再输入x,y代表马目前所在的位置,问此时的马三步之内可以到哪些地方,用#表示出来
分析:用一个step来记录步数,当步数大于3时return停掉即可。

#include<iostream>
#include<string>
using namespace std;
int dir[8][2]={{-2,-1},{-2,1},{2,-1},{2,1},{1,2},{1,-2},{-1,2},{-1,-2}} ;
char mp[10005][10005];
int m,n;
void dfs(int x,int y,int step){
	if(step>3){
		return;
	}
	if(x<0||x>=m||y<0||y>=n){
		return;
	}  
	mp[x][y]='#';
	for(int i=0;i<8;i++){
		int tx=x+dir[i][0];
		int ty=y+dir[i][1];
			dfs(tx,ty,step+1);
    }
} 
int main(){
	int x,y;
	cin>>m>>n;
	cin>>x>>y; 
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++){
			mp[i][j]='.';
		}
	}
	dfs(x-1,y-1,0);
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++){
			cout<<mp[i][j];
		}
		cout<<endl;
	}
	return 0;
}

运行结果:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值