1. 程式人生 > >NOIP2013 華容道 BFS + SPFA

NOIP2013 華容道 BFS + SPFA

傳送門

初看這題時,覺得應該就是一個大爆搜,然後調了半天也沒調出來 看了下題解才猛然醒悟…

題解: 容易發現,移動方塊就是移動空格。這樣的話,整個地圖中就只有空格及其旁邊的塊才能移動。因此,我們預處理出每一個塊向四周移動的步數,即每個方塊某個方向的空格移動到另一個方向的步數。用State[i][j][p1][p2]表示。注意:在空格移動時,不能經過當前塊。因為如果經過了,當前塊就會與空格交換位置,要交換回來必須再在原來的位置交換一次,這樣又回到了原點。求最短距離就用BFS,我們不妨在求路徑的時候直接把當前塊標記為不可移動。

然後,我們把移動的過程抽象成一張圖。當前塊和空格交換位置,其實就是兩個狀態之間的轉移。即:從當前塊空格在p方向的狀態轉移到下一個塊在p反方向的狀態。轉移的花費就相當於邊的長度。

之後,就可以用SPFA跑最短路了。

注意細節及初狀態處理,比如空格一開始的位置不一定在當前塊旁邊,要再跑一邊BFS,求出一開始的距離 以後變數名一定要準確地表意。。

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 31;
const int MAXM = 31;
const int INF = 0x3f3f3f3f;

int n, m, q;
int Plate[MAXN][MAXM], Dis[MAXN][MAXM];
int
vis[MAXN][MAXM][4], dis[MAXN][MAXM][4]; int State[MAXN][MAXM][4][4]; //目標棋子在i,j時,空白塊從p1方向移動到p2方向的距離 int movex[] = {0, 0, 1, -1}, movey[] = {1, -1, 0, 0}; inline int read(){ int k = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();} while(ch >= '0'
&& ch <= '9'){k = k*10 + ch - '0'; ch = getchar();} return k * f; } int Bfs(int ux, int uy, int Endx, int Endy){ if(ux == Endx && uy == Endy) return 0; queue<pair<int, int> > q; q.push(make_pair(ux, uy)); memset(Dis, 0x3f, sizeof(Dis)); Dis[ux][uy] = 0; while(!q.empty()){ ux = q.front().first, uy = q.front().second; q.pop(); for(int p = 0; p < 4; p++){ int vx = ux + movex[p], vy = uy + movey[p]; if(Plate[vx][vy] && Dis[vx][vy] == INF){ //可以走 Dis[vx][vy] = Dis[ux][uy] + 1; if(vx == Endx && vy == Endy){ return Dis[vx][vy]; //找到終點 } q.push(make_pair(vx, vy)); } } } return INF; } void PreWork(){ for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) if(Plate[i][j]){ Plate[i][j] = 0; for(int p1 = 0; p1 < 4; p1++) if(Plate[i + movex[p1]][j + movey[p1]]){ for(int p2 = 0; p2 < 4; p2++) if(Plate[i + movex[p2]][j + movey[p2]]){ int D = Bfs(i + movex[p1], j + movey[p1], i + movex[p2], j + movey[p2]); State[i][j][p1][p2] = D; } } Plate[i][j] = 1; } } void SPFA(int ux, int uy){ queue<int> q1; queue<pair<int, int> > q; memset(vis, false, sizeof(vis)); int pos; for(int p = 0; p < 4; p++){ q.push(make_pair(ux, uy)); q1.push(p); vis[ux][uy][p] = true; } while(!q.empty()){ ux = q.front().first, uy = q.front().second; q.pop(); pos = q1.front(); q1.pop(); vis[ux][uy][pos] = false; for(int p = 0; p < 4; p++){ int vx = ux + movex[p], vy = uy + movey[p]; if(Plate[vx][vy]){ //可以走 int D = State[ux][uy][pos][p] + 1; if(dis[vx][vy][p ^ 1] > dis[ux][uy][pos] + D){ dis[vx][vy][p ^ 1] = dis[ux][uy][pos] + D; if(!vis[vx][vy][p ^ 1]){ vis[vx][vy][p ^ 1] = true; q.push(make_pair(vx, vy)); q1.push(p ^ 1); } } } } } } int main(){ scanf("%d%d%d", &n, &m, &q); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d", &Plate[i][j]); PreWork(); int ex, ey, sx, sy, tx, ty; for(int i = 1; i <= q; i++){ scanf("%d%d%d%d%d%d", &ex, &ey, &sx, &sy, &tx, &ty); if(sx == tx && sy == ty){puts("0"); continue;} memset(dis, 0x3f, sizeof(dis)); Plate[sx][sy] = 0; for(int p = 0; p < 4; p++){ int vx = sx + movex[p], vy = sy + movey[p]; if(Plate[vx][vy]) dis[sx][sy][p] = Bfs(ex, ey, vx, vy); } Plate[sx][sy] = 1; SPFA(sx, sy); int Ans = INF; for(int p = 0; p < 4; p++) Ans = min(Ans, dis[tx][ty][p]); if(Ans == INF) puts("-1"); else printf("%d\n", Ans); } return 0; }