• 华容道题解 NOIP2013 思路题!


    第一次发紫题题解,居然在发布前太激动,把刚写好的还没发布的题解一个Ctrl+A和Backspace全删了。(所以这是二稿)

    luogu题目传送门

    前置:

    做本题一定要有的一些思想:

    1、从简思想: 模拟白格子的移动,而千万不要想这去模拟众棋子的移动。这样会简单很多,否则会s的很惨。

    2、转换思想(万物皆有其对立面):题目中给的规则,是棋子可以移动到白色格子上。我们将其共轭一下:

    白色格子可以移动到棋子上,或者说,白色格子可以和棋子交换位置。

    3、白色格子永远只有一个。(看似是废话的大实话)

    4、习惯把起点叫做x,终点叫作ht

    暴力那些事:

    暴力不难实现,就是令空白格子到处移动,然后不断对ans取min就好了。别的题解有很多详细介绍,

    在此不过多赘述。

    思路获取:

    First,从题目基本的信息入手。

    如何才能令我们的目标棋子移动?

    稍作思考。。。。。。当然是让空白格子在目标旗子旁边啊!!

    一个要点get。

    Second, 对白色格子全局的移动进行试验:

    发现,白色格子的移动可以大致分为两个阶段:不顾一切移向目标棋子,然后在棋子周围打转(因为这样才可以步数最少)

    だから(So),我们重点关注的对象,自然就来到了第二阶段上。

    不难发现,无论棋子在哪个坐标上,白色格子永远都只会有四种情况:上,下,左,右。

    移动后呢?    发现:  从一次移动到下一次移动,白色格子和棋子只会出现两大类移动:

    1、白色格子还在棋子周围,只不过是上下左右随机。

    2、白色格子直接和棋子位置进行了交换。

    再看题目要求:最短路径。

    那么,对于第一种情况,我们完全可以用BFS,求出白色格子从上一次位置移动到目前位置的距离。

    第二种情况,步数很明显是1.

    又一个要点get.

    现在考虑,如何对我们的状态进行记录?

    考虑用三维数组ok[x][y][k] (坐标x,y + 状态编号k)记录。 (其中k:k = 0,1,2,3,分别对应上下左右即可)

    最后,再看一下题目的数据范围:我们完全可以求出(以棋子为中心)所有坐标到相邻坐标的步数。

    !!!关系如此密集,何不考虑建图呢?

    如果将所有的状态看做一个点,那么他到下一个状态的边权就是他们的步数。如果我们对这个图进行SPFA呢?

    在那一串dist中,我们要求的距离,不正是终点ht周围的4个状态对应的dist吗!(取min,废话)

    重大要点get!!

    At last, 如何对这些状态进行编号,跑SPFA?不能直接用三维数组建图啊!

    你需要一个公式:((x - 1) * m + y) * 4 - (4 - k);

    至此,这道题也是被我们攻陷了。

    后置:

    1、一定要注意代码中的细节操作(特别是BFS跑状态的时候)

    2、k对应的状态可千万不能忘呀,对应错了后果很严重呀!

    码力全开!

    #include <bits/stdc++.h>
    using namespace std;
    #define N 50
    #define N2 5005
    #define isdigit(c) ((c)>='0'&&(c)<='9')
    #define INF (~0u>>1)
    const int orz = 0;
    
    inline int read(){
        int x = 0, s = 1;
        char c = getchar();
        while(!isdigit(c)){
            if(c == '-') s = -1;
            c = getchar();
        }
        while(isdigit(c)){
            x = (x << 1) + (x << 3) + (c ^ '0');
            c = getchar();
        }
        return x * s;
    }
    
    bool ma[N][N], vis[N][N];
    int fx[4] = {-1, 1, 0, 0};
    int fy[4] = {0, 0, -1, 1};
    int n, m, ex, ey, sx, sy, htx, hty;
    
    inline bool judge(int x, int y){
        if(x < 1 || y < 1 || x > n || y > m)return 0;
        return ma[x][y] ;
    }
    
    inline int getnum(int x, int y, int t){
        return ((x - 1) * m + y) * 4 - (4 - t);
    } 
    
    struct data{
        int x ,y;
        int step; /*存储每个坐标的信息*/
    };
    /* sx,sy是否可以到达htx, hty?*/
    int bfs(int dx, int dy, int sx, int sy, int htx, int hty){ 
        queue <data> q;                   /*模拟可移动格子的移动*/
        memset(vis, 0, sizeof(vis));
        q.push((data){sx, sy, 0});
        vis[sx][sy] = 1; /*从起点开始*/
        while(!q.empty()){
            data now = q.front();
            q.pop();
            int x = now.x, y = now.y, step = now.step;
            if(x == htx && y == hty){
                return now.step;   /*如果到达终点*/
            }
            for(int i = 0;i < 4; i++){
                int l = x + fx[i], r = y + fy[i];
                if(judge(l, r)){
                    if(vis[l][r] || (l == dx && r == dy))continue; /*如果新点已经访问过或者等于现在的*/
                    q.push((data){l, r, step + 1});
                    vis[l][r] = 1;
                }
            }
        }
        return INF;        /*两个点不能互相到达*/
    }
    
    struct node{
        int u, v, w;
        int next;
    } t[N2];
    int f[N2];
    
    int bian = 0;
    void add(int u, int v, int w){
        t[++bian].u = u;
        t[bian].v = v;
        t[bian].w = w;
        t[bian].next = f[u];
        f[u] = bian;
        return ;
    }
    
    /*预处理, 找出所有可行状态*/
    
    bool ok[N][N][5];
    void prepare(){     
        for(int i = 1;i <= n; i++)
            for(int j = 1;j <= m; j++)
                if(ma[i][j])
                    for(int k = 0;k < 4; k++)
                        if(judge(i+fx[k], j+fy[k]))
                            ok[i][j][k] = 1;
        for(int i = 1;i <= n; i++)    /*空白格子围绕棋子旋转时*/
            for(int j = 1;j <= m; j++)
                for(int k = 0;k < 4; k++)
                    for(int l = k + 1; l < 4; l++)  /*枚举不同的方向*/
                        if(ok[i][j][k] && ok[i][j][l]){
                            int a = getnum(i, j, k), b = getnum(i, j, l);
                            int c = bfs(i, j, i + fx[k], j + fy[k], i + fx[l], j + fy[l]);
                            if(c != INF){     /*状态之间是否可达*/
                                add(a, b, c);
                                add(b, a, c);
                            } 
                        }
        for(int i = 1;i <= n; i++)  /*空白格子和棋子交换位置*/ 
            for(int j = 1;j <= m; j++){
                if(ok[i][j][3] && ok[i][j+1][2]){
                    int a = getnum(i, j, 3);     /*几种小情况*/
                    int b = getnum(i, j+1, 2);
                    add(a, b, 1);
                    add(b, a, 1); 
                }
            }
        for(int i = 1;i <= n; i++)
            for(int j = 1;j <= m; j++){
                if(ok[i][j][1] && ok[i+1][j][0]){
                    int a = getnum(i, j, 1);
                    int b = getnum(i+1, j, 0);
                    add(a, b, 1);
                    add(b, a, 1);
                }
            }
        return ;
    } 
    
    /*进行最短路求解*/ 
    
    queue <int> q;
    bool viss[N2];
    int d[N2];
    
    int spfa(){
        memset(d, 127, sizeof(d));
        memset(viss, 0, sizeof(viss));
        int flag = d[2333]; /*之后判断用的*/
        for(int i = 0;i < 4; i++){   /*先对起点进行选取*/
            int l = sx + fx[i], r = sy + fy[i];
            if(judge(l, r)){
                int temp = bfs(sx, sy, ex, ey, l, r); /*ex,ey 是否可以到达 l,r? */
                if(temp != INF){
                    int snum = getnum(sx, sy, i);
                    viss[snum] = 1;
                    q.push(snum);
                    d[snum] = temp;
                }
            }
        }
        while(!q.empty()){   /*求所有状态间的最短路*/
            int now = q.front(); q.pop();
            viss[now] = 0;
            for(int i = f[now]; i;i = t[i].next){
                int u = t[i].u, v = t[i].v, w = t[i].w;
                if(d[v] > d[u] + w){
                    d[v] = d[u] + w;
                    if(!viss[v]){
                        viss[v] = 1;
                        q.push(v); 
                    } 
                }
            }
        }
        int ans = INF;
        for(int i = 0; i < 4; i++){
            int num = getnum(htx, hty, i);
            ans = min(ans, d[num]);
        }
        return ans == flag ? -1 : ans;
    }
    
    int main(){
    //    freopen("hh.txt", "r", stdin);
        n = read(), m = read();
        int T = read();
        for(int i = 1;i <= n; i++)
            for(int j = 1;j <= m; j++)
                ma[i][j] = read();
        prepare();
        while(T--){  /*e: 空白  s: 起点  ht:  终点*/
            ex = read(), ey = read(), sx = read(), sy = read(), htx = read(), hty = read();
            if(sx == htx && sy == hty){  /*特判*/
                puts("0");
                continue;
            }
            if(!ma[htx][hty] || !ma[sx][sy]){ /*如果起点终点根本不在图内*/
                puts("-1");
                continue;
            }
            printf("%d
    ", spfa());
        }
        return orz; /*向大佬势力低头 同时拜一下CCf求AC*/
    }

    (二稿真累)

  • 相关阅读:
    vim键盘
    Maven 插件
    Maven 快照
    Maven 常用命令
    Maven POM
    Maven 依赖机制
    Maven 中央仓库
    Maven 本地仓库
    Maven 安装配置
    Maven 简介
  • 原文地址:https://www.cnblogs.com/wondering-world/p/12723169.html
Copyright © 2020-2023  润新知