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 1≤n,m≤100,且 ( 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也得看看,去年学的都还给老师咯!
晚安。。。