NOIP2013 华容道 BFS + SPFA

版权声明:本文为DyingShu原创文章,转载请注明出处哦。 https://blog.csdn.net/DyingShu/article/details/82707184

传送门

初看这题时,觉得应该就是一个大爆搜,然后调了半天也没调出来
看了下题解才猛然醒悟…

题解:
容易发现,移动方块就是移动空格。这样的话,整个地图中就只有空格及其旁边的块才能移动。因此,我们预处理出每一个块向四周移动的步数,即每个方块某个方向的空格移动到另一个方向的步数。用 S t a t e [ i ] [ j ] [ p 1 ] [ p 2 ] 表示。注意:在空格移动时,不能经过当前块。因为如果经过了,当前块就会与空格交换位置,要交换回来必须再在原来的位置交换一次,这样又回到了原点。求最短距离就用BFS,我们不妨在求路径的时候直接把当前块标记为不可移动。

然后,我们把移动的过程抽象成一张图。当前块和空格交换位置,其实就是两个状态之间的转移。即:从当前块空格在p方向的状态转移到下一个块在p反方向的状态。转移的花费就相当于边的长度。

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

注意细节及初状态处理,比如空格一开始的位置不一定在当前块旁边,要再跑一边BFS,求出一开始的距离
以后变量名一定要准确地表意。。

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 35;
const int MAXM = 35;
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;
}

void Bfs(int ux, int uy){
    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;
                q.push(make_pair(vx, vy));
            }
        }
    }
}
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]]){
            Bfs(i + movex[p1], j + movey[p1]);
            for(int p2 = 0; p2 < 4; p2++) if(Plate[i + movex[p2]][j + movey[p2]]){
                State[i][j][p1][p2] = State[i][j][p2][p1] = Dis[i + movex[p2]][j + movey[p2]];
            }
        }
        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(){
    freopen("in.txt", "r", stdin);
    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;
        Bfs(ex, ey);
        for(int p = 0; p < 4; p++){
            int vx = sx + movex[p], vy = sy + movey[p];
            if(Plate[vx][vy]) dis[sx][sy][p] = Dis[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;
} 

update:发现数组开小了,同时加了点优化

猜你喜欢

转载自blog.csdn.net/DyingShu/article/details/82707184
今日推荐