题目描述
题目分析
对于最短步数问题,一般采用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;
}