[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 ,请看这个

代码实现细节

  1. 注意传送操作应该是 8 个方向的,而常规移动是 4 个方向。
  2. 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});
				}
			}
		}
	}
}
posted @ 2025-07-30 22:07  黑昼白夜  阅读(9)  评论(0)    收藏  举报