(Nowcoder) 牛客寒假算法基础集训营4 C Applese 走迷宫 (搜索)

视频题解戳我

传送门

题目描述

精通程序设计的 Applese 双写了一个游戏。在这个游戏中,它被困在了一个 n×mn×m 的迷宫中,它想要逃出这个迷宫。
在迷宫中,有一些方格是水池,只有当 Applese 处于水属性的时候才可以通过;有一些方格是岩浆,只有当 Applese 是火属性的时候可以通过;有一些方格是墙壁,无论如何都无法通过;另一些格子是空地(包括起点和终点),可以自由通过。在一些空地上有神秘道具可以让 Applese 转换自己的属性(从水属性变为火属性或从火属性变为水属性,需要一个单位的时间)。已知 Applese 在一个单位的时间内可以朝四个方向行走一格,且开始处于水属性,位于空地的道具拾取后只能在该处立即使用(或者不使用),且可以多次使用。求它走出迷宫需要的最少时间。

输入描述:

第一行两个正整数 n, m 表示迷宫的大小。
接下来 n 行,每行长度为 m 的字符串。描述地图。
其中 'S' 表示起点,'T' 表示终点,'.' 表示空地,'w'表示岩浆,'~'表示水池,'@' 表示道具,'#'表示障碍。
保证地图中的起点和终点只有一个,道具都位于空地。

输出描述:

输出一个整数,表示 Applese 走出迷宫的最短时间。特别地,如果 Applese 走不出迷宫,输出 "-1"。

示例1

输入

5 5
.w@..
.S#..
~w#..
.w..~
@w.~T

输出

18

备注:

1≤n,m≤100

解题思路:这是一道稍复杂的搜索题,复杂之处就是他可以改变状态,水属性和火属性,所以我们开vis数组时需要多开一维,bool vis[maxn][maxn][3]; 1为水属性,0为火属性 ,因为他这里变换状态需要一秒,所以我们不能用普通的队列了,而是优先队列,严格将时间少的放前面,(因为变换状态需要时间,所以可能会把时间多的放到队列前面),下面直接看代码吧:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=105;
int mp[maxn][maxn],n,m,sx,sy,ex,ey;
bool vis[maxn][maxn][3]; //1为水属性,0为火属性 
int ans=inf;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
struct node{
	int x;
	int y;
	int step;
	int state;
	bool operator < (const node &x) const{
		return step > x.step;
	}
}; 
bool pd(int nx,int ny,int nsta){ //第一步判断是否可走
	if(!vis[nx][ny][nsta] && mp[nx][ny]!=-1 && nx>=1 && nx<=n
	&& ny>=1 && ny<=m)	return true;
	else	return false;
}
bool solve(int sx,int sy){
	priority_queue<node> q;
	q.push(node{sx,sy,0,1}); //初始为水属性
	while(!q.empty()){
		node np=q.top();
		q.pop();
		int x=np.x,y=np.y,nstep=np.step,nsta=np.state;
		if(x==ex && y==ey){
			ans=nstep;
			return true;
		}
		for(int i=0;i<4;++i){
			int nx=x+dx[i],ny=y+dy[i];
			if(pd(nx,ny,nsta)){
				if(mp[nx][ny]==0)	q.push(node{nx,ny,nstep+1,nsta}),vis[nx][ny][nsta]=1;
				else if(mp[nx][ny]==nsta+1)	q.push(node{nx,ny,nstep+1,nsta}),vis[nx][ny][nsta]=1;
                        //属性相符可走,属性和标记刚好差1
				else if(mp[nx][ny]==3){ //变换属性,变或者不变
					q.push(node{nx,ny,nstep+1,nsta}),vis[nx][ny][nsta]=1;
					q.push(node{nx,ny,nstep+2,1-nsta}),vis[nx][ny][1-nsta]=1;
				}	
			}
		} 
	}
	return false;
}
int main(){
	std::ios::sync_with_stdio(0);
	cin>>n>>m;
	char tp;
	for(int i=1;i<=n;++i){ //将对应的字符变成对应数字
		for(int j=1;j<=m;++j){
			cin>>tp;
			if(tp=='S')	sx=i,sy=j,mp[i][j]=0;
			else if(tp=='T')	ex=i,ey=j,mp[i][j]=0;
			else if(tp=='.')	mp[i][j]=0;
			else if(tp=='w')	mp[i][j]=1;	
			else if(tp=='~')	mp[i][j]=2;
			else if(tp=='@')	mp[i][j]=3;
			else if(tp=='#')	mp[i][j]=-1;
		}
	}
	if(solve(sx,sy))	cout<<ans<<endl;
	else	cout<<-1<<endl;
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值