中國計量大學現代科技學院第四屆“中競杯”程式設計校賽 I 題 (雙端佇列bfs / 優先佇列bfs)
阿新 • • 發佈:2020-12-06
題目連結:https://ac.nowcoder.com/acm/contest/9680/I
記錄一下上次移動的方向,如果這次移動的方向和上次相同,那麼邊權為 \(0\), 否則邊權為 \(1\)
邊權僅為 \(0/1\) 的圖求最短路徑可以使用雙端佇列 \(bfs\),如果邊權為 \(0\) 就加入隊首,否則加入隊尾,
這樣可以保證佇列中距離的二段性和單調性,使得第一次到達目標狀態時即為最優答案
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<cmath> #include<queue> using namespace std; typedef long long ll; const int maxn = 5010; int n, m; int sx, sy, ex, ey; int a[maxn][maxn]; int dis[maxn][maxn]; char s[maxn]; int dx[4] = {0, 0, 1, -1}; int dy[4] = {1, -1, 0, 0}; struct Node{ int x, y, step, dir; }; deque<Node> q; void bfs(){ memset(dis, 0x3f, sizeof(dis)); Node start; start.x = sx, start.y = sy; start.step = 0; start.dir = -1; dis[sx][sy] = 0; q.push_front(start); while(!q.empty()){ Node u = q.front(); q.pop_front(); if(u.x == ex && u.y == ey){ printf("%d\n", u.step); return; } if(u.step > dis[u.x][u.y]) continue; // 之前放入佇列的狀態已經被更新過了,當前狀態無用,佇列中後面相同狀態的更小的dis來更新 for(int i = 0 ; i < 4 ; ++i){ int x = u.x + dx[i], y = u.y + dy[i]; if(x > 0 && x <= n && y > 0 && y <= m && a[x][y] != 1){ Node now; now.x = x, now.y = y, now.dir = i; if(i != u.dir && u.dir != -1){ now.step = u.step + 1; if(dis[x][y] > now.step){ // 一定要加 $dis$ 的限制,更優的狀態才可以被更新,否則會超限 dis[x][y] = now.step; q.push_back(now); } } else{ now.step = u.step; if(dis[x][y] > now.step){ dis[x][y] = now.step; q.push_front(now); } } } } } printf("-1\n"); } ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; } int main(){ n = read(), m = read(); sx = read(), sy = read(), ex = read(), ey = read(); ++sx, ++sy, ++ex, ++ey; for(int i = 1 ; i <= n ; ++i){ scanf("%s", s + 1); for(int j = 1 ; j <= m ; ++j){ a[i][j] = s[j] - '0'; } } bfs(); return 0; }