NOIP2013 華容道 BFS + SPFA
阿新 • • 發佈:2018-12-09
初看這題時,覺得應該就是一個大爆搜,然後調了半天也沒調出來 看了下題解才猛然醒悟…
題解: 容易發現,移動方塊就是移動空格。這樣的話,整個地圖中就只有空格及其旁邊的塊才能移動。因此,我們預處理出每一個塊向四周移動的步數,即每個方塊某個方向的空格移動到另一個方向的步數。用表示。注意:在空格移動時,不能經過當前塊。因為如果經過了,當前塊就會與空格交換位置,要交換回來必須再在原來的位置交換一次,這樣又回到了原點。求最短距離就用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;
}