首页 > 其他分享 >「NOIP2013」华容道

「NOIP2013」华容道

时间:2025-01-18 17:33:11浏览次数:1  
标签:dist 格子 int 华容道 短路 空格 NOIP2013 const

solution by XiangXunYi

题目描述

给你一张华容道,有障碍格,共 \(q\) 次询问,每次指定一个起点,终点和空格,问最少需要多少步让起点棋子移到终点。

思路推导

step 1

先思考暴力,发现状态与当前格子和空格的位置有关系,同时问最少步数,故采用最短路,定义 \(dis_{x,y,p,q}\) 表示当前格子位置 \((x,y)\) ,空格位置 \((p,q)\) 的最短路。
时间复杂度 \(O(n^4T)\)。

step 2

我们可以发现只动一个格子且只有当空格在其附近才能动,所以只有空格与该格子四联通时的状态是有意义的,所以可以将后两位的 \(p,q\) 改为 \(p \in \{0,1,2,3\}\),即定义 \(dis_{x,y,0/1/2/3}\) 表示当前格子在 \((x,y)\) ,空格在其上/下/左/右 时的最短路,因为边少,使用 dijkstra 更优,接下来考虑建图。

step 3

对于一个格子和其附近空格,有两种情况:

  • 交换空格与格子,即从 \(i,j,p\) 向 \(i+dx[p],j+dy[p],p \bigoplus 1\) 连边
  • 改变空格的位置,即从 \(i,j,p\) 向 \(i,j,q\) 连边

对于第一种情况,显然边权为 \(1\),但对于第二种情况,要在不交换 \(i,j\) 的情况下将空格从相对位置 \(p\) 交换到相对位置 \(q\),可以采取 bfs 且指定一点不能走(后面还有用)。
明显一开始空格可能不在其四联通分量中,所以要将空格移过来,枚举空格相对位置,并限制不能走到 \((sx,sy)\),将答案加上在此情况下的最短路,即是最终答案。
tips: 当 \((sx,sy) \neq (tx,ty)\) 时才需要将空格迁移。

solution

定义 \(dis_{i,j,p}\) 表示格子在 \((i,j)\),空格相对在其上/下/左/右时的最短路,跑 dijkstra,预处理 \(f_{i,j,p,x,y}\) 表示点 \((i,j)\) 在不经过其上/下/左/右时到达 \((x,y)\) 的最短路,最终答案为 \(min^4_{p=1} \{ dis_{tx,ty,p} \} + min^4_{p=1} \{ f_{sx+dx[p],sy+dy[p],p^1,tx,ty} \}\) 。

代码

文字不多,代码挺长,自己打调还是难,不是同学帮忙估计还要多 \(1\) ~ \(2 h\) 。

/*
address:https://www.becoder.com.cn/problem/9288 or https://www.luogu.com.cn/problem/P1979
AC 2024/11/27 15:26
*/
#include<bits/stdc++.h>
using namespace std;
const int dx[] = { 0,0,1,-1,0 }, dy[] = { 1,-1,0,0,0 };
const int N = 35, M = N * N << 2;
const int INF = 0x3f3f3f3f;
int n, m, Q;
int a[N][N];
struct edge {
    int to, w;
};
vector<edge>G[M];
int dist[M];
bool vis[M];
struct node {
    int u, dist;
    bool operator < (const node& o)const { return dist > o.dist; }
};
priority_queue<node>qq;
inline void dijkstra(int s) {
    fill(dist + 1, dist + (n * m << 2) + 1, INF);
    fill(vis + 1, vis + (n * m << 2) + 1, false);
    dist[s] = 0;
    qq.push({ s,0 });
    while (!qq.empty()) {
        int u = qq.top().u;
        qq.pop();
        if (vis[u]) continue;
        vis[u] = true;
        for (auto e : G[u])
            if (dist[e.to] > dist[u] + e.w) {
                dist[e.to] = dist[u] + e.w;
                qq.push({ e.to,dist[e.to] });
            }
    }
}
inline bool check(int x, int y) { return x > 0 && x <= n && y > 0 && y <= m && a[x][y] == 1; }
int cnt;
int dis[N][N];
pair<int, int> qqq[N * N];
inline void bfs(int s, int t, int nx, int ny) {
    int l = 1, r = 1;
    qqq[r++] = { s,t };
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= m;j++) dis[i][j] = 0x3f3f3f3f;
    dis[s][t] = 0;
    while (l < r) {
        int x = qqq[l].first, y = qqq[l].second;
        l++;
        for (int i = 0;i < 4;i++) {
            int kx = x + dx[i], ky = y + dy[i];
            if (!check(kx, ky) || kx == nx && ky == ny || dis[kx][ky] != 0x3f3f3f3f) continue;
            dis[kx][ky] = dis[x][y] + 1;
            qqq[r++] = { kx,ky };
        }
    }
}
int f[N][N][4][N][N], id[N][N][4];
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", &a[i][j]);
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= m;j++)
            if (check(i, j))
                for (int k = 0;k < 4;k++) {
                    int x = i + dx[k], y = j + dy[k];
                    if (check(x, y)) {
                        bfs(i, j, x, y);
                        for (int p = 1;p <= n;p++)
                            for (int q = 1;q <= m;q++)
                                f[i][j][k][p][q] = dis[p][q];
                    }
                }
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= m;j++)
            if (check(i, j))
                for (int p = 0;p < 4;p++)
                    if (check(i + dx[p], j + dy[p]))
                        id[i][j][p] = ++cnt;
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= m;j++)
            if (check(i, j))
                for (int p = 0;p < 4;p++) {
                    if (check(i + dx[p], j + dy[p])) {
                        for (int q = 0;q < 4;q++)
                            if (p != q && check(i + dx[q], j + dy[q]))
                                G[id[i][j][p]].push_back({ id[i][j][q],f[i + dx[p]][j + dy[p]][p ^ 1][i + dx[q]][j + dy[q]] });
                        G[id[i][j][p]].push_back({ id[i + dx[p]][j + dy[p]][p ^ 1],1 });
                    }
                }
    while (Q--) {
        int ex, ey, sx, sy, tx, ty;scanf("%d%d%d%d%d%d", &ex, &ey, &sx, &sy, &tx, &ty);
        int ans = 0x3f3f3f3f;
        for (int p = 0;p < 4;p++) {
            if (!check(sx + dx[p], sy + dy[p])) continue;
            int cur = (sx == tx && sy == ty) ? 0 : f[sx + dx[p]][sy + dy[p]][p ^ 1][ex][ey];
            dijkstra(id[sx][sy][p]);
            int mn = 0x3f3f3f3f;
            for (int q = 0;q < 4;q++)
                if (check(tx + dx[q], ty + dy[q])) mn = min(mn, dist[id[tx][ty][q]]);
            cur += mn;
            ans = min(ans, cur);
        }
        printf("%d\n", ans >= 0x3f3f3f3f ? -1 : ans);
    }
    return 0;
}

特别鸣谢 XiangXunYi 帮调。

标签:dist,格子,int,华容道,短路,空格,NOIP2013,const
From: https://www.cnblogs.com/keysky/p/18678637

相关文章

  • P1982 [NOIP2013 普及组] 小朋友的数字 题解
    题目传送锚点在博客园食用更佳题意有一列小朋友,他们每个人都有一个值。定义每个小朋友的特征值为祂及祂前面人值的最大子段和。又定义每个小朋友的数字为祂前面人中的一个人的特征值加本身值的最大值。。。思路把题意用人话说出来即为思路:先输入每个小朋友的值\(a_i\);再......
  • 题解:P1970 [NOIP2013 提高组] 花匠
    闲话本文同步发布在cnblogs。正题容易发现此题要求花必须一高一低摆放。最优化问题,看不出怎么贪心,遂DP。设计状态\(f_{i,0}\)表示当前为上升形势最长花序列,\(f_{i,1}\)表示当前为下降形势最长花序列。状态转移由于需要一高一低,易得:\[f_{i,0}=\begin{cases}......
  • 项目8:简单数字华容道 --- 《跟着小王学Python·新手》
    项目8:简单数字华容道—《跟着小王学Python·新手》《跟着小王学Python》是一套精心设计的Python学习教程,适合各个层次的学习者。本教程从基础语法入手,逐步深入到高级应用,以实例驱动的方式,帮助学习者逐步掌握Python的核心概念。通过开发游戏、构建Web应用、编写网络爬......
  • P1979 [NOIP2013 提高组] 华容道
    题目大意详细题目传送门\(n\timesm\)的华容道盘,有障碍。多组询问,每组障碍不变。其中要将初始在\((sx,sy)\)的棋子移动到\((tx,ty)\)。初始空白的位置在\((ex,ey)\)。求至少多少次移动完成目标,无法完成输出-1。\(n,m\leq30,q\leq500\)。思路发现显然应该是要预处理什......
  • 洛谷 P1983 [NOIP2013 普及组] 车站分级(拓扑排序)
    题目传送门解题思路对于每一趟列车,我们可以知道其中没有经过的车站的级别肯定会比经过的车站的级别低。于是,我们可以根据这种关系来建一个图。将等级小的车站往等级大的车站建边。于是,我们可以发现这是一个DAG(有向无环图),所以我们可以拓扑排序。我们从等级最小的车站......
  • 洛谷题单指南-分治与倍增-P1966 [NOIP2013 提高组] 火柴排队
    原题链接:https://www.luogu.com.cn/problem/P1966题意解读:计算两个序列∑(ai​−bi​)^2的最小值,对10^8-3取模。解题思路:1、贪心思路要使得两个序列对应位置元素之差的平方和最小,必须满足两个序列相对排序是一致的,什么意思?设a序列有两个元素:a1,a2,b序列有两个元素b1,b2当a1<a2,b......
  • Java基础课设,大作业,小游戏--------数字华容道[无偿提供源代码]100%可以运行
    成品游戏胜利1.准备图片0.png1.png2.png3.png4.png5.png6.png7.png8.png9.png10.png11.png12.png13.png......
  • 洛谷P1983 [NOIP2013 普及组] 车站分级 题解
    思路由题可知,在一趟车次的区间内,停靠的站点的等级恒大于不停靠的站点。因此,对于每一趟车次的区间,给所有停靠的站点向所有不停靠的站点两两连有向边,然后求图中最长的路径长度,就能得到答案。实现因为可能出现重边,而且\(n\le1000\),所以在处理车次连边的时候使用邻接矩阵,再改成邻......
  • P1967 [NOIP2013 提高组] 货车运输
    原题链接题解朴素做法:每次询问,二分最小边,然后bfs遍历查看是否能到达,时间复杂度\(O(q\cdotlogn\cdotm)\)优化:如果答案里的最小边是\(k\),那么代表所有边权不小于\(k\)的边都可以使用,因此可以直接从大到小加入边,直至起点与终点连接时间复杂度\(O(q\cdotm\cdotlogm......
  • [题解]P1967 [NOIP2013 提高组] 货车运输
    P1967[NOIP2013提高组]货车运输题意简述给定一个\(N\)个节点,\(M\)条边的无向图,其中每条边有一个边权。接下来给定\(q\)次询问。每次询问给出\(x,y\),请计算\(x\)到\(y\)路径上最小边权的最大值是多少。解题思路我们对于每个连通块跑一遍最大生成树。这样整张图就成了一片森......