「AMPPZ2014」The Captain

首先我们有一个朴素的最短路做法,对于 \(u(x_u, y_u)\)\(v(x_v, y_v)\) 连一条边权为 \(\min(|x_u - x_v|, |y_u - y_v|)\) 的边,进一步发现在最短路的条件下可以转化为分别连边权为 \(|x_u - x_v|\)\(|y_u - y_v|\) 的边。

考虑优化边数,因为我没想到只需连在横坐标上和纵坐标上最近的四点即可,所以我考虑构造一个建图方案使得所有边能不漏地对应上去。

考虑到走边可以看作先选定一个方向,在该方向上左右移动,花费移动距离的代价,再走到当前轴上对应的随便一个点,所以有对于连边 \(u(x_u, y_u)\), \(v(x_v, y_v)\) 在坐标轴上对应为下图(红边代表原本连边,蓝边代表改后连边)

然后发现对于所有从 \(u\) 连出的边共享一条边权为 \(0\) 的边,所有从 \(v\) 连入的边共享一条边权为 \(0\) 的边,然后在 \(x\) 轴和 \(y\) 轴上的边共享一条链,如下图

最后对 \(x\) 轴和 \(y\) 轴上的边离散化一下就得到了总边数 \(O(N)\) 的图,并且完美包含原图所有路径。

/*
address:https://qoj.ac/problem/11560
AC 2026/7/22 14:53
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 5;
const LL INF = 1e18;
struct edge { int to, w; };
vector<edge>G[N];
int n;
int x[N], y[N];
int disc1[N], m1, disc2[N], m2;
inline void build_graph() {
    for (int i = 1;i <= n;++i) {
        G[i].push_back({ x[i] + n, 0 });
        G[x[i] + n].push_back({ i, 0 });
        G[i].push_back({ y[i] + m1 + n, 0 });
        G[y[i] + m1 + n].push_back({ i, 0 });
    }
    for (int i = 1;i < m1;++i) {
        G[i + n].push_back({ i + 1 + n, disc1[i + 1] - disc1[i] });
        G[i + 1 + n].push_back({ i + n, disc1[i + 1] - disc1[i] });
    }
    for (int i = 1;i < m2;++i) {
        G[i + n + m1].push_back({ i + 1 + n + m1, disc2[i + 1] - disc2[i] });
        G[i + 1 + n + m1].push_back({ i + n + m1, disc2[i + 1] - disc2[i] });
    }
}
struct node {
    int u;
    LL dist;
    bool operator < (const node& o)const { return dist > o.dist; }
};
priority_queue<node>q;
LL dis[N];
bool vis[N];
inline void dijkstra() {
    for (int i = 1;i <= n + m1 + m2;++i) dis[i] = INF, vis[i] = false;
    dis[1] = 0;
    q.push({ 1, 0 });
    while (!q.empty()) {
        int u = q.top().u;
        q.pop();
        if (vis[u]) continue;
        vis[u] = true;
        for (auto [v, w] : G[u])
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                q.push({ v, dis[v] });
            }
    }
}
inline void init(int* disc, int& m, int* x) {
    for (int i = 1;i <= n;++i) disc[++m] = x[i];
    sort(disc + 1, disc + m + 1);
    m = unique(disc + 1, disc + m + 1) - disc - 1;
    for (int i = 1;i <= n;++i) x[i] = lower_bound(disc + 1, disc + m + 1, x[i]) - disc;
}
int main() {
    scanf("%d", &n);
    for (int i = 1;i <= n;++i) scanf("%d%d", &x[i], &y[i]);
    init(disc1, m1, x), init(disc2, m2, y);
    build_graph();
    dijkstra();
    printf("%lld", dis[n]);
    return 0;
}
posted @ 2026-07-22 22:04  keysky  阅读(2)  评论(0)    收藏  举报