【2024.10.18练习】迷宫与陷阱

题目描述


题目分析

对于最短步数问题,一般采用BFS搜索。在状态转移时依次分析下个格子的各种情况。

剪枝:对于普通状态,之前以普通状态访问过的格子不需再访问。对于无敌状态:如果当前格子已经被访问过,且之前到达该格子时的无敌状态剩余步数比现在要多,则不需要再次访问该格子。


我的代码

#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
const int MAX_N = 1005;
char map[MAX_N][MAX_N]; //正常状态下的地图 
int sign[MAX_N][MAX_N]; //记录走过格子时的状态
int step[MAX_N][MAX_N]; //记录步数  
int n;
int k;
typedef pair<int,int> P;
queue<P> que; 
//地图初始化 
void init_map(){
	for(int i=0;i<MAX_N;i++){
		for(int j=0;j<MAX_N;j++){
			map[i][j]='#';
			sign[i][j]=-1; //没走过 
		}
	}
}
//广搜 
int dx[4]={-1,0,0,1};
int dy[4]={0,1,-1,0};
int bfs(int x,int y){
	sign[x][y]=0;
	step[x][y]=0; 
	que.push(P(x,y));
	while(que.size()){
		x = que.front().first;
		y = que.front().second;
		que.pop();
		for(int i=0;i<4;i++){
			int nx = x+dx[i];
			int ny = y+dy[i];
			//下一格有无敌道具 且之前的状态没捡过 
			if(map[nx][ny]=='%'&&sign[nx][ny]==-1){
				sign[nx][ny]=k;
				step[nx][ny]=step[x][y]+1;
				que.push(P(nx,ny));
			//下一格有通路 且没在当前最多无敌步数状态下走过 
			}else if(map[nx][ny]=='.'&&max(sign[x][y]-1,0)>sign[nx][ny]){
				sign[nx][ny]=max(sign[x][y]-1,0);
				step[nx][ny]=step[x][y]+1;
				que.push(P(nx,ny));
			//下一格有陷阱 且当前状态为无敌 
			}else if(map[nx][ny]=='X'&&sign[x][y]>0){
				sign[nx][ny]=max(sign[x][y]-1,0);
				step[nx][ny]=step[x][y]+1;
				que.push(P(nx,ny));
			}	 
			//已到达终点
			if(nx==n&&ny==n){
				return step[nx][ny];
			} 
		}	
	}
	return -1;
}
//主函数 
int main()
{
	init_map();
	//Input
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>map[i][j];
		}
	}
	//Algorithm
	cout<<bfs(1,1);
	return 0;
}

思路扩展

上述代码的bfs队列中储存的状态只有坐标,而其余状态细节则建立二维数组独立存储。这种做法在每个格子只访问一次的情况下是没有问题的。然而一旦在bfs中对同一个节点访问多次,则可能覆盖之前的状态,导致结果步数出错。因此最好将状态细节一起储存到结构体中,再将结构体作为元素储存在队列中更加安全。

不过这样做依然需要二维数组sign[i][j]存储访问过的最大无敌步数,用于剪枝。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
const int MAX_N = 1005;
char map[MAX_N][MAX_N]; //正常状态下的地图 
int sign[MAX_N][MAX_N]; //记录走过格子时的状态
int n;
int k;
struct Node{ //结构体储存状态信息:坐标/当前步数/剩余无敌步数 
	int x,y,step,magic;
}; 
queue<Node> que; 

//地图初始化 
void init_map(){
	for(int i=0;i<MAX_N;i++){
		for(int j=0;j<MAX_N;j++){
			map[i][j]='#';
			sign[i][j]=-1; //没走过 
		}
	}
}
//广搜 
int dx[4]={-1,0,0,1};
int dy[4]={0,1,-1,0};
int bfs(int x,int y){
	sign[x][y]=0;
	que.push({1,1,0,0}); //起点状态入队 
	while(que.size()){
		Node node = que.front(); //取状态 
		x = node.x;
		y = node.y;
		int step = node.step;
		int magic= node.magic; 
		que.pop();
		//已到达终点
		if(x==n&&y==n){
			return step;
		} 
		//状态转移 
		for(int i=0;i<4;i++){
			int nx = x+dx[i];
			int ny = y+dy[i];
			//下一格有无敌道具 且之前的状态没捡过 
			if(map[nx][ny]=='%'&&sign[nx][ny]==-1){
				sign[nx][ny]=k;
				que.push({nx,ny,step+1,k});
			//下一格有通路 且没在当前最多无敌步数状态下走过 
			}else if(map[nx][ny]=='.'&&max(magic-1,0)>sign[nx][ny]){
				sign[nx][ny]=max(magic-1,0);
				que.push({nx,ny,step+1,max(magic-1,0)});
			//下一格有陷阱 且当前状态为无敌 
			}else if(map[nx][ny]=='X'&&magic>0){
				sign[nx][ny]=max(magic-1,0);
				que.push({nx,ny,step+1,max(magic-1,0)});
			}	 
		}	
	}
	return -1;
}
//主函数 
int main()
{
	init_map();
	//Input
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>map[i][j];
		}
	}
	//Algorithm
	cout<<bfs(1,1);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值