蓝桥备赛 3.26-3.27 简单迷宫(DFS+回溯)迷宫与陷阱(BFS+三维数组)

3.26
ok,今天依然是8点闹钟 9.30醒 晕乎乎的
突然想起来简单看一下去年看过的迷宫,用的是dfs+回溯,简单回顾一下
新手期慢慢来
B3625 迷宫寻路

题目描述

机器猫被困在一个矩形迷宫里。
迷宫可以视为一个 n × m n\times m n×m 矩阵,每个位置要么是空地,要么是墙。机器猫只能从一个空地走到其上、下、左、右的空地。
机器猫初始时位于 ( 1 , 1 ) (1, 1) (1,1) 的位置,问能否走到 ( n , m ) (n, m) (n,m) 位置。

输入格式

第一行,两个正整数 n , m n,m n,m
接下来 n n n 行,输入这个迷宫。每行输入一个长为 m m m 的字符串,# 表示墙,. 表示空地。

输出格式

仅一行,一个字符串。如果机器猫能走到 ( n , m ) (n, m) (n,m),则输出 Yes;否则输出 No

样例 #1

样例输入 #1

3 5
.##.#
.#...
...#.

样例输出 #1

Yes
数据规模与约定

对于 100 % 100\% 100% 的数据,保证 1 ≤ n , m ≤ 100 1 \leq n, m \leq 100 1n,m100,且 ( 1 , 1 ) (1,1) (1,1) ( n , m ) (n, m) (n,m) 均为空地。

解:
一个比较简单的深搜
(实际上就是暴力啦)
首先定义一个方向数组,用于控制上下左右的移动

int a[]={1,0,-1,0};
int b[]={0,-1,0,1};

设置一个数组用于记录该点是否可以被访问

vis[ ][ ] //初始均为0,输入#为墙壁则置为1,表示不可访问,每走过一个点也要设置为1

接下来就是重点的 void dfs( ) 功能函数,注意判断有两个边界

1、若是已经到达终点,则return;
2、不能超出迷宫的边界(这个写在for循环里)

完整AC代码如下所示:

#include<iostream>
using namespace std;

int a[]={1,0,-1,0};
int b[]={0,-1,0,1};
char arr[105][105];
int vis[105][105]; 
int n,m;
int pp,qq;
int flag;

void dfs(int p,int q){
	
	if(flag) return;
	//访问该点 
	vis[p][q]=1;
	if(p==n&&q==m) {
		flag=1; 
		return;
	} 
	for(int i=0;i<4;i++){
		pp=p+a[i];
		qq=q+b[i];
		if(pp<1||qq<1||pp>n||qq>m) 
			continue;//回溯 
		if(vis[pp][qq]==0)//墙壁处为1不可访问 
			dfs(pp,qq);
	}
	
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			cin>>arr[i][j];
			if(arr[i][j]=='#') vis[i][j]=1;
		}
			
	dfs(1,1);
		
	if(flag) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	
	return 0;
}

后来看到一道进阶题,P8673 [蓝桥杯 2018 国 C] 迷宫与陷阱
由于他是要求最短路径,做了半天后发现一个问题:

他可能重复地走某一段路,然后完成最短路径的目标
因为他有“免死金牌”和陷阱
比如去另一个方向获得无敌值再回来

我在模板题的做法基础上稍加改进后还没成功,后来就搁置了
准备偷学一下题解,看到要用队列和三维数组!然后就又搁置了一下
先立个flag!今天搞完!
下午又铲了两把,又十钢了!可惜对面被队友(我女朋友说是她)送走了没给我爽
手替:
[蓝桥杯2018决赛]迷宫与陷阱 //这篇和我的思路最像!!!
第九届蓝桥杯(国赛)——迷宫与陷阱 //这篇有空再看看
……

还是不要乱立flag,今天估计看不完了,明天也是满课,歇两天
回宿舍开撸LOL!

3.27
今天一天满课,除了五六 五六节回去睡觉了
在上课闲暇又看了一点题解
主要提出了三个问题

1、如何防止进入死循环?
2、如何确保可以往回走?
3、怎样确保是最短路径?

先来说一下解决方案:

1、创建bool vis[a][b][c]三维数组,用于记录在走到(x,y)且无敌次数还剩 c 次时是否走过该点,若在 c-1 次时走过则不用再走,continue,防止进入死循环

2、发生“往回走”情况的前提是走到了“%”,即获得了无敌,此问题也是依靠bool vis[a][b][c]三维数组解决,若是c-1次没走,那我们在c次时就可以尝试走,实现了往回走的目的

3、困扰我最久的是怎么确保就是最短路径呢?到底是很久没看算法了,问题出在BFS,因为BFS是一层一层地向外搜索,所以一旦队列(BFS的底层就是队列实现)中的某个元素到达终点就可以直接输出,也一定就是最短距离,可以参考下面的讲解感受一下BFS 我当时看一眼就顿悟了,所以有时候还是得多角度看问题

传送门:DFS与BFS算法

具体的代码等今晚或者明天再更~~ 课多有点累>_< ||

3.28 周四 一天没课 出去玩了

3.29 几乎也是一天的课,晚上8点到9点半打了篮球,现在刚回来想着续更一下
对于这道题,主要的精髓就在于三维数组和BFS,可惜的是洛谷有两道测试题的内存超了,结果如下图:
在这里插入图片描述

代码如下所示:

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;

int a[]={1,0,-1,0};
int b[]={0,-1,0,1};
char arr[1005][1005];
bool vis[1005][1005][12]; 
//vis[a][b][c]表示处于(a,b)且无敌值为c时是否访问 
//bool默认为 false,即均未访问 
bool flag;
int N,k;

struct Node{
	int x;
	int y;
	int wudi;
	int step;
};

queue<Node> q;

void bfs(int m,int n){ 
	Node node;
	node.x=m;
	node.y=n;
	node.wudi=0;
	node.step=0;
	q.push(node);
	vis[1][1][0]=true;
	
	//压入起始点,接下来开始BFS寻找最短路径 
	
	while(!q.empty()){//若队列中还有节点则继续 
		Node p=q.front(); //取q队列的队首元素 
		q.pop();//弹出队首元素 
		if(p.x==N && p.y==N){//若能到达必是最短路径,直接输出即可 
			flag=true;
			cout<<p.step<<endl;
			return;
		}
		for(int i=0;i<4;i++){//四个方向的遍历 
			int xp=p.x+a[i];
			int yp=p.y+b[i];
			if( xp>=1 && yp>=1 && xp<=N && yp<=N && arr[xp][yp]!='#' ){//控制边界条件 ,大前提 
				if(arr[xp][yp]=='%'&&vis[xp][yp][k]==0) {//走到道具格 
					vis[xp][yp][k]==1;
					Node pp;
					pp.x=xp;
					pp.y=yp;
					pp.wudi=k;
					pp.step=p.step+1;
					q.push(pp);
				}
				else if(arr[xp][yp]=='X' && p.wudi && vis[xp][yp][p.wudi-1]==0){//这步表示回头重新走之前因无敌值不够而没走的 
					vis[xp][yp][p.wudi-1]==1;
					Node pp;
					pp.x=xp;
					pp.y=yp;
					pp.wudi=p.wudi-1;
					pp.step=p.step+1;
					q.push(pp);
				}
				else if(arr[xp][yp]=='.' && p.wudi && vis[xp][yp][p.wudi-1]==0) {//接下来是正常格子的两种走法 
					vis[xp][yp][p.wudi-1]=1;
					Node pp;
					pp.x=xp;
					pp.y=yp;
					pp.step=p.step+1;
					pp.wudi=p.wudi-1;
					q.push(pp);
				}
				else if(arr[xp][yp]=='.' && p.wudi==0 && vis[xp][yp][0]==0)
				{
					vis[xp][yp][0]=1;
					Node pp;
					pp.x=xp;
					pp.y=yp;
					pp.step=p.step+1;
					pp.wudi=0;
					q.push(pp);
				}
				else continue;
			}	
		}
	} 
}

int main(){
	cin>>N>>k;
	
	for(int i=1;i<=N;i++)
		for(int j=1;j<=N;j++)
			cin>>arr[i][j];
		
	bfs(1,1);
	if(!flag) cout<<"-1"<<endl;
	
	return 0;
} 

应该是少了剪枝操作,明天再看吧,好累~~ (比赛能拿80也够了)
明天还得再做一下BFS的板子题巩固一下,还有KMP和Floyd也得看看,去年学的都还给老师咯!
晚安。。。

  • 58
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Haoyu Xiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值