P9351 [JOI 2023 Final] Maze
P9351 [JOI 2023 Final] Maze
[JOI 2023 Final] Maze
题面翻译
给定一张 \(R\times C\) 的地图,其中 .
可以走,而 #
不能走。一次操作可以将 \(N \times N\) 的正方形范围内所有点变成 .
,给定起点和终点,求最少需要几次操作使得起点和终点连通(只能上下左右移动)。
\(R\times C\le 6\times 10^6\),\(N\le R\le C\)。
模拟赛思维题
首先不难想到,对于每个点,它可以走到路径是一个正方形被挖掉四个角:
1 | 1 | 1 | ||||||
1 | 1 | 1 | 1 | 1 | ||||
1 | 1 | X | 1 | 1 | ||||
1 | 1 | 1 | 1 | 1 | ||||
1 | 1 | 1 | ||||||
观察这个图,我们不难发现,所有边缘的点到x点的“八联通距离”是相等的
并且所有满足“八连通距离”$ \in [1,n] $ 的点都在这个图上。
我们用高度来描述每次盖章:
每次盖章之后,这个点的高度变为n
之后每走一步(无论黑白)高度都减少1
只有当高度大于0时可跨过黑点
高度等于0时只能走白点
有了这个性质,我们对每次bfs记录一个状态:
{x,y,dis,h}
分别表示每个点的横纵坐标,到这个点要盖几次章,到这个点的高度
然后对于每个点,只要它的高度不为0,就先消耗1的高度向四周bfs,如果高度为0,判断目标点是否为黑点:
若是黑点,则dis++,h=n-1(在u点应为n,在v点就是n-1)
若是白点,则直接走过去
然后注意一个细节:因为这题横纵坐标很大,要么用动态内存把二维压成一维,或者用map实现
但是因为map自带一个log,然后这题有点卡常,貌似过不去
所以要开数组然后二维压成一维
还有就是这题我bfs好像实现的不是很好,所以当数据出道极限(r=6e6,c=1)时,id可能出现12e6导致RE所以我直接把数组开到了12e6懒得重新写特判
然后这题就做完了
Code:
#include<bits/stdc++.h> #define mp(x,y) ((x-1)*m+y) const int N=2e7+5; using namespace std; int Map[N]; int vis[N]; int dx4[4]={-1,0,1,0},dy4[4]={0,1,0,-1}; int dx8[8]={-1,-1,-1,0,1,1,1,0},dy8[8]={-1,0,1,1,1,0,-1,-1}; char c[N]; int n,m,k,sx,sy,ex,ey; bool check(int x,int y) { if(x<1||n<x)return 0; if(y<1||m<y)return 0; return 1; } struct Node{ int x,y,dis,h; }; deque<Node> Q; void bfs() { Q.push_back({sx,sy,1,0}); while(!Q.empty()) { Node u=Q.front();Q.pop_front(); if(!check(u.x,u.y))continue; if(vis[mp(u.x,u.y)])continue; vis[mp(u.x,u.y)]=u.dis; if(u.x==ex&&u.y==ey)return ; if(u.h) { for(int i=0;i<8;i++) { Node v={dx8[i]+u.x,dy8[i]+u.y,u.dis,u.h-1}; if(check(v.x,v.y)&&(!vis[mp(v.x,v.y)])) { Q.push_back(v); } } } else { for(int i=0;i<4;i++) { Node v={dx4[i]+u.x,dy4[i]+u.y,u.dis+Map[mp(dx4[i]+u.x,dy4[i]+u.y)],0}; if(check(v.x,v.y)&&(!vis[mp(v.x,v.y)])) { if(Map[mp(v.x,v.y)]){v.h=k-1;Q.push_back(v);} else {Q.push_front(v);} } } } } } void solve() { cin>>n>>m>>k; cin>>sx>>sy; cin>>ex>>ey; for(int i=1;i<=n;i++) { scanf("%s",c+1); for(int j=1;j<=m;j++) { Map[mp(i,j)]=(c[j]=='#'); } } bfs(); printf("%d",vis[mp(ex,ey)]-1); } int main() { //freopen("P9351.in","r",stdin);//freopen("P9351.out","w",stdout); solve(); return 0; }