多种 迷宫问题 与 多种 解法

①①①①①①①①①①①①①①①①①①①①①①①①①①①①①①①①①①

在这里插入图片描述
在这里插入图片描述

Example

Input
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000

Output
DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRUUURRRR
DDDDRDRRRRRURRRDRRDDDRRRRUURUUUUUUUULLLUUUURRRRUUL
LLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDD
LDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR

代码(BFS广搜)

#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>
#include <queue>
#include <stack>
#include <map>  
#include <set>

#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("D:\\in.txt","r",stdin)
#define FO freopen("D:\\out.txt","w",stdout)

using namespace std;
typedef long long LL;
/*-------下面为主要代码-------*/

char a[30][50];	//地图
bool visits[30][50];	//标记该点是否走过
int dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};	//方向数组按照上,下,左,右的顺序走
char dirc[4] = {'U','D','L','R'};
int n,m;  	//迷宫的行列

struct Step
{
	int x;	//横坐标 
	int y;	//纵坐标 
	string str;	//路径 
	Step(int xx, int yy, string s):x(xx),y(yy),str(s){}; 
}; 

queue<Step> q; //创建队列

bool check(int x, int y) //判断是否越界 以及是否是墙 以及是否访问过了 
{
	if (x < 0 || x >= 30 || y < 0 || y >= 50 || visits[x][y] || a[x][y] == '1')  //注意数组 a 是 char型,所以是 '1'
		return false;
	return true;
}

void bfs(int x, int y)
{
	q.push(Step(x, y, ""));
	while (!q.empty())
	{
		Step p = q.front();
		if (p.x == 29 && p.y == 49)
		{	//到达终点了 
			cout << p.str << endl;
			break;
		}
		q.pop(); //放在这里是因为不能放在 for循环里面(细品)
		_for(i,0,4)
		{
			int nx = p.x + dir[i][0];
			int ny = p.y + dir[i][1];
			if (check(nx, ny))
			{
				q.push(Step(nx, ny, p.str + dirc[i]));
				visits[nx][ny] = true; //标记
			}
		}
	}
} 

int main()
{
	_for(i,0,30)
	{
		scanf("%s", a[i]);
	}
	bfs(0, 0);
	return 0;
}

解析

1、dir 的作用是存储移动的方向,代码中用一个int dir[4][2]代替两个int dir_1[4]int dir_2[4],这是一种简便的存储方式
2、注意存储的地图的数组是 char 还是 int
3、代码中移动的坐标轴是要逆时针转90o的,因为终点是(29,49),故 U 是横坐标 -1,纵坐标不变,其他方向以此类推
4、注意 main 函数中 for 循环只用循环 30 次是因为 %s ,循环一次即输入一行

②②②②②②②②②②②②②②②②②②②②②②②②②②②②②②②②②②

题目

老鼠走迷宫是递回求解的基本题型,我们在二维阵列中使用 2 表示迷宫墙壁,使用 1 表示老鼠行走的路径,试求出图中由入口(1,1)至出口(5,5)的路径并打印出来
在这里插入图片描述

Example

Output
#######
#     #
# # # #
#  # ##
## # ##
#     #
#######

显示路径:
#######
#1    #
#1# # #
#11# ##
##1# ##
# 1111#
#######

代码(DFS深搜)

#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>
#include <queue>
#include <stack>
#include <map>  
#include <set>

#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("D:\\in.txt","r",stdin)
#define FO freopen("D:\\out.txt","w",stdout)

using namespace std;
typedef long long LL;
/*-------下面为主要代码-------*/

int maze[7][7] = {
    {2, 2, 2, 2, 2, 2, 2},
    {2, 0, 0, 0, 0, 0, 2},
    {2, 0, 2, 0, 2, 0, 2},
    {2, 0, 0, 2, 0, 2, 2},
    {2, 2, 0, 2, 0, 2, 2},
    {2, 0, 0, 0, 0, 0, 2},
    {2, 2, 2, 2, 2, 2, 2} 
};
int stuff = 0;
int dfs(int x,int y) //由于 stuff 的存在故是 int 类型函数
{
	maze[x][y] = 1;
	if(x==5&&y==5)
	{
		stuff = 1; //找到迷宫出口,并标记 stuff 为 1
	}
	else
	{
		if(stuff!=1&&maze[x][y+1]==0) dfs(x,y+1);
		if(stuff!=1&&maze[x][y-1]==0) dfs(x,y-1);
		if(stuff!=1&&maze[x+1][y]==0) dfs(x+1,y);
		if(stuff!=1&&maze[x-1][y]==0) dfs(x-1,y);
	}
	if(stuff!=1) maze[x][y]=0; //若此路径最后没有找到出口则取消 stuff 标记
	return stuff;
}

int main()
{
	_for(i,0,7) //打印迷宫
	{
		_for(j,0,7)
		{
			if(maze[i][j])
			{
				cout<<'#';
				continue;
			}
			cout<<' ';
		}
		cout<<endl;
	}
	
	if(dfs(1,1) == 0)
        cout<<endl<<"没有找到出口!\n";
    else
    {
        cout<<endl<<"显示路径:\n";
        _for(i,0,7) //打印迷宫老鼠行走路径
        {
            _for(j,0,7)
            {
                if(maze[i][j] == 2) cout<<'#';
                else if(maze[i][j] == 1) cout<<'1';
                else cout<<' ';
            }
            cout<<endl;       
        }
    }
	return 0;
}

解析

本题代码核心就在于 stuff,每行走一条路径并在路径上标记为 1,直到找到迷宫出口,若最后没有找到迷宫(即 stuff 不为 1 )则删除此路上的标记 1,并且 dfs()函数最后 return 的不是 1 的话就说明没有找到出口,可以根据此返回值决定是否打印路径

③③③③③③③③③③③③③③③③③③③③③③③③③③③③③③③③③③

题目

有一个 m行,n列的迷宫,其中用1表示可以走,0表示不可以走,输入迷宫m * n的数据和起始点、结束点的坐标,现在要你输出找出所有可行的道路(要求所走的路中没有重复的点,走时只能是上下左右四个方向,若一条路都不可行,则输出 -1)
输入
第一行是两个数m,n(1< m,n< 15),接下来由是 m 行 n 列且由 1 和 0 组成的数据,最后两行是起始点和结束点的坐标
输出
所有可行的路径,输出时按照左上右下的顺序,描述一个点时用(x,y)的形式,除开始点外,其他的都要用->表示,若没有一条可行的路则输出 -1

Example

Input
5 4
1 1 0 0
1 1 1 1
0 1 1 0
1 1 0 1
1 1 1 1
1 1
5 4

Output
(1,1)->(1,2)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(1,2)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
(1,1)->(1,2)->(2,2)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(1,2)->(2,2)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)

代码(DFS深搜)

#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>
#include <queue>
#include <stack>
#include <map>  
#include <set>

#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("D:\\in.txt","r",stdin)
#define FO freopen("D:\\out.txt","w",stdout)

using namespace std;
typedef long long LL;
/*-------下面为主要代码-------*/

const int MAXN = 15; //题目规定(1< m,n< 15),最大可取是 14,故数组最大可取 15(包含 0)
const int devide = 100; //用于分隔一个数字的两重含义,后面会讲
int maze[MAXN][MAXN]; //原数组
int a[MAXN*MAXN]; //存储坐标的数组
int visited[MAXN][MAXN]; //标记路径的数组
int m,n,x,y,r,s; //分别代表 行,列,起始坐标(x,y),终点坐标(r,x)
int flag; //判断函数是否找到迷宫终点
int dir[4][2] = {{0,-1},{-1,0},{0,1},{1,0}}; //注意题目要求左上右下

bool check(int nx,int ny,int m,int n) //判断是否越界 以及是否是墙 以及是否访问过了 
{
	if(nx<0 || nx>m || ny<0 || ny>n || !maze[nx][ny] || visited[nx][ny]) return false;
	return true;
}

void dfs(int x,int y,int step)
{
    if(x == r && y == s)
    {
        flag = 1;
        for(int i = 0;i < step;i ++)
        {
            int now = a[i];
            cout<<"("<<now/devide<<","<<now%devide<<")->";            
        }
        cout<<"("<<x<<","<<y<<")"<<endl;
    }
    else 
    {
        int x = a[step] / devide;
        int y = a[step] % devide;
        //左上右下
        
        _for(i,0,4) //改进代码,更加简便
        {
        	int nx = x+dir[i][0];
        	int ny = y+dir[i][1];
        	if(check(nx,ny,m,n))
        	{
        		a[step + 1] = nx * devide + ny;
            	visited[nx][ny] = 1;
            	dfs(nx,ny,step+1);
            	visited[nx][ny] = 0; //删除标记
			}
		}
		//下面是 4 个 if语句是原代码
        /*
        if(y - 1 > 0 && maze[x][y-1] && !visited[x][y-1])
        {
            a[step + 1] = x * devide + y - 1;
            visited[x][y - 1] = 1;
            dfs(x,y - 1,step + 1);
            visited[x][y - 1] = 0;
        }
        if(x - 1 > 0 && maze[x-1][y] && !visited[x-1][y])
        {
            a[step + 1] = (x - 1) * devide + y;
            visited[x-1][y] = 1;
            dfs(x-1,y,step+1);
            visited[x-1][y] = 0;
        }
        if(y + 1 <= n && maze[x][y+1] && !visited[x][y+1])
        {
            a[step + 1] = x * devide + y + 1;
            visited[x][y+1] = 1;
            dfs(x,y+1,step + 1);
            visited[x][y+1] = 0;
        }
        if(x + 1 <= m && maze[x+1][y] && !visited[x+1][y])
        {
            a[step + 1] = (x + 1) * devide + y;
            visited[x+1][y] = 1;
            dfs(x+1,y,step+1);
            visited[x+1][y] = 0;
        }
        */
    }
}

int main()
{
    cin>>m>>n;
    _for(i,1,m+1) _for(j,1,n+1) cin>>maze[i][j];
    cin>>x>>y>>r>>s;
    a[0] = x * devide + y;
    visited[x][y] = 1;
    dfs(x,y,0);

    if(!flag)
        cout<<-1<<endl;

    return 0;
}

解析

1、这道题的代码很强的一点在于用一维数组记录坐标,具体方法是先设置一个长度最大(15 * 15)的数组,然后每搜索一次数组元素加一次,元素为 nx * 100 + ny,nx 与 ny 为下一次搜索的坐标(nx,ny)若最后找到迷宫出口要输出坐标时,用 for 循环遍历数组所有元素,该元素值 除以 100 即 nx,除余 100 即 ny,每一条路径的数组 a[ ] 都不一样(故遍历的元素值也不一样)
2、那么为什么要设置 devide 为 100 呢?假设一种情况为 ny 等于 14,那么要是 devide 为 10 ,该元素值除余 10 就不等于 14 了,而是 4,换而言之,devide 的位数必须要大于 ny 的最大位数,比如题目要求(1< m,n< 100)那么 devide 就是 1000
3、注意题目的 Example 中每个路径的起始点都是从(1,1)开始,那么 maze 数组也要从(1,1)开始记录有效元素值;还有注意输出规定是左上右下,那么搜索顺序也应该是左上右下
4、可以思考一下为什么要删除坐标,稍微动一下脑想象一下就知道了

④④④④④④④④④④④④④④④④④④④④④④④④④④④④④④④④④④

题目

Dr.Kong设计的机器人卡多非常爱玩,它常常偷偷跑出实验室,在某个游乐场玩之不疲。这天卡多又跑出来了,在SJTL游乐场玩个不停,坐完碰碰车,又玩滑滑梯,这时卡多又走入一个迷宫。整个迷宫是用一个N * N的方阵给出,方阵中单元格中填充了一个整数,表示走到这个位置的难度,这个迷宫可以向上走,向下走,向右走,向左走,但是不能穿越对角线,走迷宫的取胜规则很有意思,看谁能更快地找到一条路径,其路径上单元格最大难度值与最小难度值之差是最小的,虽然这样的路径或许不是最短路径,机器人卡多现在在迷宫的左上角(第一行,第一列)而出口在迷宫的右下角(第N行,第N列)你能找到这样的一条路径吗?
输入
有多组测试数据,以EOF为输入结束的标志
第一行: N 表示迷宫是N * N方阵 (2 ≤ N ≤ 100)
接下来有N行, 每一行包含N个整数,用来表示每个单元格中难度 (0 ≤ 任意难度 ≤ 120)
输出
输出为一个整数,表示路径上最高难度与和最低难度的差

Example

Input
5
1 1 3 6 8
1 2 2 5 5
4 4 0 3 3
8 0 2 3 4
4 3 0 2 1

Output
2

代码(DFS深搜)

#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>
#include <queue>
#include <stack>
#include <map>  
#include <set>

#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("D:\\in.txt","r",stdin)
#define FO freopen("D:\\out.txt","w",stdout)

using namespace std;
typedef long long LL;
/*-------下面为主要代码-------*/

int n;
int mp[150][150]; //原地图
int vis[105][105]; //用于路径标记
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int max_,min_; //存储在范围内的最大值与最小值,用于 dfs排除路径,后面会讲

bool check(int nx,int ny)
{
	if(nx>=0&&nx<n&&ny>=0&&ny<n&&vis[nx][ny]==0) return true;
	return false;
}

int dfs(int x,int y)
{
	int q,i;
	if(mp[x][y]>max_||mp[x][y]<min_) return 0; //一旦有不在范围内的元素,立刻否定当前路径 
	if(x==n-1&&y==n-1) return 1;
	for(i=0;i<4;i++)
	{
		int nx=x+dir[i][0];
		int ny=y+dir[i][1];
		if(check(nx,ny))
		{
			vis[nx][ny]=1;
			q=dfs(nx,ny); //q的作用是确认是否存在这样一条路径,只需要一条即可 dfs返回 1
			if(q==1) return 1; //注意判断语句要放在这个位置
		}
	}
	//不能放在这个位置,因为 q在循环中会迭代赋值
	return 0;
}
int judge(int x) //列举所有不超过 120的 max_和 min_
{
	int i,q;
	for(i=0;i+x<120;i++) //如当差值为 60时,min_有 0,1,2... ,max_有 60,61,62...
	{
		memset(vis,0,sizeof(vis)); //取消 vir数组的所有标记
		min_=i;
		max_=i+x;
		vis[0][0]=1; //别忘了标记原点
		q=dfs(0,0);
		vis[0][0]=0; //别忘了删除原点标记
		if(q==1) return 1; //只要满足一种差值的情况,即可证明该差值存在
	}
	return 0;
}


int main()
{
	while(scanf("%d",&n)!=EOF) //记住这种写法,用于多组测试数据 
	{
		int i,j;
		_for(i,0,n) _for(j,0,n) scanf("%d",&mp[i][j]);
		int l=0,r=120,mid;
		while(l<=r)     //二分法列举各种差值,当循环结束时必定满足 l=r 
		{
			mid=(l+r)/2;
			if(judge(mid)) r=mid-1;
			else l=mid+1;
		}
		printf("%d\n",l); //因为 l=r,因此输出r也行 
	}
	return 0;
}

解析

本题代码的思路非常新奇(至少对我来说),主要思路是先确认各种差值,再判断是否存在这样的路径,下面来详解

1、确认差值用二分法:因为题目规定差值满足(0 ≤ 任意难度(差值) ≤ 120),故差值先取 60,若满足代表还存在更高的差值,若不存在则说明差值太高,直到确认到只有一种差值为止(最多只用 7 次)
2、判断是否存在这样存在的路径:先列举出所有的差值的情况 max_ - min_ ,一 一 代入 dfs 中搜索,只需要满足 一条路径 即可证明 该差值的情况存在,这又代表了至少存在一条这样的路径,那么又可以退回二分法那里,寻找更高的差值,以此类推…
3、vis 数组在这道题(DFS)要标记,是因为这个迷宫没有终点也没有障碍,题目要求也很特别

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值