题解:AT_abc420_d [ABC420D] Toggle Maze
使用广度优先搜索解决此题。与一般的 BFS 不同,经过 ? 点会改变某些点是否可以通过。
考虑一扇门是否开启仅与走到 ? 点的次数的奇偶性相关,因此我们可以在搜索的状态中记录一个变量 open 表示当前是否让本来关着的门打开,本来打开的门关上。
但是直接搜索会 TLE,比如会在某个点重复的经过,可以记录一个 map<pair<pair<int,int>,bool>> vis 表示某个状态是否被访问过。如果已经访问过那么之前就已经搜索过了这个状态,就不用继续搜索了。
::::info[代码]
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define up(l,r,i) for(int i=(l);(i)<=(r);++i)
#define pii pair<ll,ll>
constexpr int N=509;
ll n,m,sx,sy,ex,ey;
char a[N][N];
map<pair<pii,bool>,bool> vis;
struct node{
ll x,y,step;
bool open;
};
void bfs(){
queue<node> q;
q.push({sx,sy,0,0});
while(!q.empty()){
node tmp=q.front();q.pop();
if(vis[{ {tmp.x,tmp.y},tmp.open}])continue;
vis[{ {tmp.x,tmp.y},tmp.open}]=1;
if(tmp.x==ex&&tmp.y==ey){
cout<<tmp.step<<"\n";
return;
}
up(-1,1,i){
up(-1,1,j){
if(abs(i)+abs(j)!=1)continue;
ll tx=tmp.x+i,ty=tmp.y+j;
if(tx<1||tx>n||ty<1||ty>m)continue;
if(a[tx][ty]=='#')continue;
if(a[tx][ty]=='.'||a[tx][ty]=='G'||a[tx][ty]=='S'){
q.push({tx,ty,tmp.step+1,tmp.open});
}
else if(a[tx][ty]=='?'){
q.push({tx,ty,tmp.step+1,!tmp.open});
}
else if(a[tx][ty]=='o'&&tmp.open==0){
q.push({tx,ty,tmp.step+1,tmp.open});
}
else if(a[tx][ty]=='x'&&tmp.open==1){
q.push({tx,ty,tmp.step+1,tmp.open});
}
}
}
}
cout<<"-1\n";
}
int main()
{
cin>>n>>m;
up(1,n,i){
up(1,m,j){
cin>>a[i][j];
if(a[i][j]=='S')sx=i,sy=j;
if(a[i][j]=='G')ex=i,ey=j;
}
}
bfs();
return 0;
}
::::