题意
黄水题(
传送门:
引用站外地址
[ABC176D] Wizard in Maze
洛谷
给出一个包含 #
和 .
的地图,有两种移动方式:一种是在 .
间移动,一种是在移动到以当前点为中心的 5x5
的矩阵内的任意 .
。问至少需要进行多少次第二种移动才能从给定的 (sx,sy) 走到 (dx,dy)。如果无解输出 -1
。(这一点题目中并没有说,是在WA了两个点之后发现的)
解析
初版
考虑使用 BFS
来搜索路径。我们将 .
第一种移动花费的代价设为 0
,第二种花费代价则为 1
。问题就转换为了求矩阵最短路问题,最后得到的最小代价即为第二种移动的最少次数,然后输出后 break
即可。
可以将当前的花费代价、x 和 y 存在一个 pair<int,pair<int,int>>
中,然后放在队列里面进行搜索。
不同于图上最短路,这类矩阵最短路的代价是在队列存储的 pair
中传递的,显然这会导致可能出现最先搜到的并不是最优解,那么我们需要记录 ans=min(cnt,ans)
最后输出,如果 ans==INF
就输出 -1
否则输出 ans
。
6分代码
然后很容易得到一段朴素的 BFS 代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <bits/stdc++.h> using namespace std; int n,m,sx,sy,dx,dy,cnt,ans=1e9; const int fx[]={1,0,-1,0},fy[]={0,1,0,-1}; char c[1005][1005]; int vis[1005][1005]; queue<pair<int,pair<int,int>>> q; int main(){ ios::sync_with_stdio(0); cin>>n>>m>>sx>>sy>>dx>>dy; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>c[i][j]; q.push(make_pair(0,make_pair(sx,sy))); while(!q.empty()){ auto p=q.front(); q.pop(); if(p.second.first==dx&&p.second.second==dy) ans=min(ans,p.first); vis[p.second.first][p.second.second]=1; for(int t=0;t<4;t++){ int nx=p.second.first+fx[t],ny=p.second.second+fy[t]; if(c[nx][ny]=='#') continue; if(nx<1||ny<1||nx>n||ny>m) continue; if(vis[nx][ny]) continue; q.push(make_pair(p.first,make_pair(nx,ny))); } for(int i=p.second.first-2;i<=p.second.first+2;i++) for(int j=p.second.second-2;j<=p.second.second+2;j++){ if(i<1||j<1||i>n||i>m) continue; if(c[i][j]=='#') continue; if(vis[i][j]) continue; q.push(make_pair(p.first+1,make_pair(i,j))); } } cout<<(ans==1e9?-1:ans); return 0; }
|
恭喜你喜提6分 TLE:
优化1
显然这需要优化。
考虑贪心,每一次尽量找代价最小的进行转移。
如果使用数组暴力肯定会炸,可以使用优先队列和双端队列。
因为代价只有 0
和 1
,显然当代价为 0
时放到双端队列队头,为 1
是放在队尾,就可以保证它内部是有序的,每次取出队头扩展时也一定是最近的,就不需要记录 ans
了,找到直接输出即可。
当然也可以使用优先队列,但是这里双端队列的复杂度仅为 O(1),而优先队列为 O(logn),稍比 deque
高,所以优先考虑 deque
。
很容易改写出双端队列版。
42分代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <bits/stdc++.h> using namespace std; int n,m,sx,sy,dx,dy,cnt; const int fx[]={1,0,-1,0},fy[]={0,1,0,-1}; char c[1005][1005]; int vis[1005][1005]; deque<pair<int,pair<int,int>>> q; int main(){ ios::sync_with_stdio(0); cin>>n>>m>>sx>>sy>>dx>>dy; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>c[i][j]; q.push_back(make_pair(0,make_pair(sx,sy))); while(!q.empty()){ auto p=q.front(); q.pop_front(); if(p.second.first==dx&&p.second.second==dy) cout<<p.first,exit(0); vis[p.second.first][p.second.second]=1; for(int t=0;t<4;t++){ int nx=p.second.first+fx[t],ny=p.second.second+fy[t]; if(c[nx][ny]=='#') continue; if(nx<1||ny<1||nx>n||ny>m) continue; if(vis[nx][ny]) continue; q.push_front(make_pair(p.first,make_pair(nx,ny))); } for(int i=p.second.first-2;i<=p.second.first+2;i++) for(int j=p.second.second-2;j<=p.second.second+2;j++){ if(i<1||j<1||i>n||i>m) continue; if(c[i][j]=='#') continue; if(vis[i][j]) continue; q.push_back(make_pair(p.first+1,make_pair(i,j))); } } cout<<-1; return 0; }
|
这份代码只有42分。会爆彩虹(AC+TLE+MLE)。
卡常+玄学部分の优化3
上一份代码只有42分,但是我们已经优化过了,原因会让人感到非常玄学。
经过了一番BFS和DFS的结合体之后,我想到一种优化方案:
问题出在这一句上,方案2的转移如果每次都循环 25
次未免浪费时间了,遇到超出坐标范围时应该直接在循环边界里面限制下,就可以卡掉一个 25
的时间和空间常数(估计是这么多实际上可能只有 5~10
的):
1
| if(i<1||j<1||i>n||i>m) continue;
|
我们删掉这一句,把循环的边界改为:
1 2
| for(int i=max(p.second.first-2,1);i<=min(p.second.first+2,n);i++) for(int j=max(p.second.second-2,1);j<=min(m,p.second.second+2);j++){
|
这道题就通过了。
卡常万岁!!!
代码
注释版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <bits/stdc++.h> using namespace std; int n,m,sx,sy,dx,dy; const int fx[]={1,0,-1,0},fy[]={0,1,0,-1}; char c[1005][1005]; int vis[1005][1005]; deque<pair<int,pair<int,int>>> q; int main(){ ios::sync_with_stdio(0); cin>>n>>m>>sx>>sy>>dx>>dy; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>c[i][j]; q.push_back(make_pair(0,make_pair(sx,sy))); while(!q.empty()){ auto p=q.front(); q.pop_front(); if(p.second.first==dx&&p.second.second==dy) cout<<p.first,exit(0); vis[p.second.first][p.second.second]=1; for(int t=0;t<4;t++){ int nx=p.second.first+fx[t],ny=p.second.second+fy[t]; if(c[nx][ny]=='#') continue; if(nx<1||ny<1||nx>n||ny>m) continue; if(vis[nx][ny]) continue; q.push_front(make_pair(p.first,make_pair(nx,ny))); } for(int i=max(p.second.first-2,1);i<=min(p.second.first+2,n);i++) for(int j=max(p.second.second-2,1);j<=min(m,p.second.second+2);j++){ if(c[i][j]=='#') continue; if(vis[i][j]) continue; q.push_back(make_pair(p.first+1,make_pair(i,j))); } } cout<<-1; return 0; }
|
无注释版
可以CTJ的版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <bits/stdc++.h> using namespace std; int n,m,sx,sy,dx,dy,cnt; const int fx[]={1,0,-1,0},fy[]={0,1,0,-1}; char c[1005][1005]; int vis[1005][1005]; deque<pair<int,pair<int,int>>> q; int main(){ ios::sync_with_stdio(0); cin>>n>>m>>sx>>sy>>dx>>dy; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>c[i][j]; q.push_back(make_pair(0,make_pair(sx,sy))); while(!q.empty()){ auto p=q.front(); q.pop_front(); if(p.second.first==dx&&p.second.second==dy) cout<<p.first,exit(0); vis[p.second.first][p.second.second]=1; for(int t=0;t<4;t++){ int nx=p.second.first+fx[t],ny=p.second.second+fy[t]; if(c[nx][ny]=='#') continue; if(nx<1||ny<1||nx>n||ny>m) continue; if(vis[nx][ny]) continue; q.push_front(make_pair(p.first,make_pair(nx,ny))); } for(int i=max(p.second.first-2,1);i<=min(p.second.first+2,n);i++) for(int j=max(p.second.second-2,1);j<=min(m,p.second.second+2);j++){ if(c[i][j]=='#') continue; if(vis[i][j]) continue; q.push_back(make_pair(p.first+1,make_pair(i,j))); } } cout<<-1; return 0; }
|
优先队列版
没事干写的
实测复杂度带的 log 会被卡掉:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <bits/stdc++.h> using namespace std; int n,m,sx,sy,dx,dy; const int fx[]={1,0,-1,0},fy[]={0,1,0,-1}; char c[1005][1005]; int vis[1005][1005]; priority_queue<pair<int,pair<int,int>>,vector<pair<int,pair<int,int>>>,greater<pair<int,pair<int,int>>>> q; int main(){ ios::sync_with_stdio(0); cin>>n>>m>>sx>>sy>>dx>>dy; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>c[i][j]; q.push(make_pair(0,make_pair(sx,sy))); while(!q.empty()){ auto p=q.top(); q.pop(); if(p.second.first==dx&&p.second.second==dy) cout<<p.first,exit(0); vis[p.second.first][p.second.second]=1; for(int t=0;t<4;t++){ int nx=p.second.first+fx[t],ny=p.second.second+fy[t]; if(c[nx][ny]=='#') continue; if(nx<1||ny<1||nx>n||ny>m) continue; if(vis[nx][ny]) continue; q.push(make_pair(p.first,make_pair(nx,ny))); } for(int i=max(p.second.first-2,1);i<=min(p.second.first+2,n);i++) for(int j=max(p.second.second-2,1);j<=min(m,p.second.second+2);j++){ if(c[i][j]=='#') continue; if(vis[i][j]) continue; q.push(make_pair(p.first+1,make_pair(i,j))); } } cout<<-1; return 0; }
|
各位大佬们点个赞嘛qwq~