[JOI 2023 Final] 迷宫 / Maze
题意简述
给定一张 \(R \times C\) 的地图,其中 \(\tt.\) 可以走,而 \(\tt\#\) 不能走。一次操作可以将 \(N \times N\) 的正方形范围内所有点变成 \(\tt.\),给定起点和终点,求最少需要几次操作使得起点和终点连通(只能上下左右移动)。
暴力
这题第一个关键点就是想到怎么把修改操作与移动操作统一起来,这里我们考虑把修改操作改成可以随机传送到这个 \(N \times N\) 正方形的任意位置(虽然我也不知道是怎么想出来的),很容易证明这样操作是不重不漏的,因为如果你进行两次修改操作选择了在同一个正方形的两个点,一定会被上次操作覆盖。
那么 \(O(rcn^2)\) 的暴力就很好写了,就是一个正常的 bfs,瓶颈在于传送的 \(O(n^2)\)。
考虑优化。
正解
在我们将修改操作和移动操作同化后,其实有个很重要的点是,bfs 的转移过程中边权都是 0 和 1,这个时候就要用到 01bfs 了,如果你还不会 01bfs ,请看这个。
代码实现细节
- 注意传送操作应该是 8 个方向的,而常规移动是 4 个方向。
- bfs 的过程中要存一维变量处理传送移动的信息。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=6e6+10;
int dx[]={0,0,1,-1,1,1,-1,-1},dy[]={1,-1,0,0,1,-1,1,-1};
int n,m,k,sx,sy,tx,ty;
bool arr[N],vis[N];
bool in(int x,int y) {
return(x>=1&&x<=n&&y>=1&&y<=m);
}
int id(int x,int y) {
return(x-1)*m+y;
}
struct node {
int x,y,t,h;
};
int main() {
cin>>n>>m>>k>>sx>>sy>>tx>>ty;
for(int i=1; i<=n; i++) {
string str;
cin>>str;
for(int j=1; j<=m; j++)arr[id(i,j)]=(str[j-1]=='#');
}
deque<node>Q(1,(node) {
sx,sy,0,0
});
while(1) {
node N=Q.front();
Q.pop_front();
int x=N.x,y=N.y,t=N.t,h=N.h;
if(vis[id(x,y)])continue;
vis[id(x,y)]=true;
if(x==tx&&y==ty) {
cout<<t<<endl;
return 0;
}
if(h) {
for(int d=0; d<=7; d++) {
int xx=x+dx[d],yy=y+dy[d];
if(in(xx,yy)&&!vis[id(xx,yy)])
Q.push_back((node) {xx,yy,t,h-1});
}
} else {
for(int d=0; d<=3; d++) {
int xx=x+dx[d],yy=y+dy[d];
if(in(xx,yy)&&!vis[id(xx,yy)]) {
if(arr[id(xx,yy)])Q.push_back((node) {xx,yy,t+1,k-1});
else Q.push_front((node) {xx,yy,t,0});
}
}
}
}
}

浙公网安备 33010602011771号