- Sources:D-Drunken Maze
- Abstract:給定
n
×
m
(
12
≤
n
×
m
≤
2
×
1
0
5
,
3
≤
n
,
m
≤
1
0
4
)
n\times m(12\le n\times m\le 2\times 10^5, 3\le n,m\le 10^4)
n×m(12≤n×m≤2×105,3≤n,m≤104)的地圖,
# 表示墻壁,. 表示空地,S 表示起點,T 表示終點。要求不能超過連續
3
3
3步走同一方向,不可以通過障礙。起點終點視作空地。問 S 到 T 的最小步數。若無解輸出
?
1
-1
?1。 - Keyword:搜索(銅牌題)
- Solution:注意到
n
n
n和
m
m
m非常大,因此無法用結構體存圖。改用動態處理隊列。設計狀態數組
d
i
s
t
dist
dist,注意到圖中每個點可通過求解其
i
d
id
id進而將狀態數組壓至三維。
d
i
s
t
[
i
d
]
[
d
i
r
]
[
c
n
t
]
dist[id][dir][cnt]
dist[id][dir][cnt]代表沿
d
i
r
dir
dir方向上經過
c
n
t
cnt
cnt次到達頂點
i
d
id
id。注意處理當
c
n
t
cnt
cnt到達
3
3
3時不可繼續沿此方向繼續走。
- Code:實現上可采用
B
F
S
BFS
BFS,或
D
F
S
DFS
DFS+記憶化。下面給出
B
F
S
BFS
BFS實現。
#include<bits/stdc++.h>
using namespace std;
const int MAX=1e4;
const int INF=1e9;
char mapp[MAX][MAX];
int n,m,sy,sx,ty,tx;
struct node{
int y,x,pre_dir,cnt;
};
queue<node>q;
int getid(int y,int x){
return y*m+x;
}
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
bool check1(int y,int x){
return y>=0&&y<n&&x>=0&&x<m&&mapp[y][x]!='#';
}
bool check2(int y,int x){
return x==tx&&y==ty;
}
void bfs(){
vector<vector<vector<int>>>dis(n*m,vector<vector<int>>(4,vector<int>(3,INF)));
for(int i=0;i<4;i++){
int ny=sy+dir[i][0],nx=sx+dir[i][1];
if(check1(ny,nx)){
int id=getid(ny,nx);
if(dis[id][i][0]>1){
dis[id][i][0]=1;
q.push({ny,nx,i,1});
}
}
}
bool f=0;
while(q.size()){
int ans=dis[getid(q.front().y,q.front().x)][q.front().pre_dir][q.front().cnt-1];
if(check2(q.front().y,q.front().x)){
cout<<ans,f=1;
break;
}
for(int i=0;i<4;i++){
int now=0;
if(i==q.front().pre_dir){
now=q.front().cnt+1;
if(now>3) continue;
}else now=1;
int ny=q.front().y+dir[i][0],nx=q.front().x+dir[i][1];
if(check1(ny,nx)){
if(dis[getid(ny,nx)][i][now-1]>ans+1){
dis[getid(ny,nx)][i][now-1]=ans+1;
q.push({ny,nx,i,now});
}
}
}
q.pop();
}
if(!f) cout<<-1;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
cin>>mapp[i][j];
if(mapp[i][j]=='S') sy=i,sx=j;
else if(mapp[i][j]=='T') ty=i,tx=j;
}
bfs();
return 0;
}