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;
}