CF1422D

首先联想到最短路,

以每个转折点为顶点,两两连一条边,再加上起点与终点,就可以了。

但这样边数至少有 $O(m^2)$,会爆炸,

考虑优化:我们直连一个点与它上下左右相邻最近的点就行了,

举个例子:

有三个转折点:$A(1, 2)$,$B (3, 3)$,$C (4, 7)$,要从 $A$ 走到 $C$,

  1. 直接走:花费 $3$ 的代价

  2. 先走 $B$,在走 $C$,花费 $2 + 1 = 3$ 的代价

两种走法一样,就可以不连 $AC$ 这条边。

而 $B$ 更接近于 $A$。

四个方向,有 $O (4m)$ 条边,加上起点与终点,共 $O(6m + 1)$ 条边,

最短路大家都很熟,这里不讲了。

#include <queue> 
#include <cstdio>
#include <iostream>
#include <algorithm> 
using namespace std;
#define int long long

const int N = 1e5 + 10;
const int inf = (1 << 30);

struct node {
    int x, y, id;
} t[N];

struct Node {
    int w, to, nxt;
} edge[N << 3];
int cnt, head[N];

int n, m;
int sx, sy, tx, ty;

int dis[N];
bool vis[N];

priority_queue <pair <int, int> > q;

bool cmpx (node p, node q) {
    return p.x < q.x;
}

bool cmpy (node p, node q) {
    return p.y < q.y;
}

void add (int u, int v, int w) {
    cnt ++;
    edge[cnt].w = w;
    edge[cnt].to = v;
    edge[cnt].nxt = head[u];
    head[u] = cnt;
}

void dij () { // 最短路
    memset (dis, 0x7f, sizeof (dis));
    dis[0] = 0;
    q.push (make_pair (0, 0));
    while (!q.empty()) {
        int u = q.top().second; int d = -q.top().first; q.pop(); // 由于是大根堆,所以取负的存,使得小的先
        if (vis[u]) continue;
        vis[u] = 1;
        for (int i = head[u]; i; i = edge[i].nxt) {
            int v = edge[i].to, w = edge[i].w;
            if (dis[v] > d + w) {
                dis[v] = d + w;
                q.push (make_pair (-dis[v], v));
            }
        }
    }
    printf ("%d", dis[m + 1]);
}

int main() {
    scanf ("%d%d", &n, &m);
    scanf ("%d%d%d%d", &sx, &sy, &tx, &ty);
    for (int i = 1; i <= m; ++i) {
        scanf ("%d%d", &t[i].x, &t[i].y); t[i].id = i;
    }
    sort (t + 1, t + m + 1, cmpx); // 按照 x 轴排序,保证在 x 轴情况下,最接近
    for (int i = 1; i <= m - 1; ++i) {
        add (t[i].id, t[i + 1].id, t[i + 1].x - t[i].x);
        add (t[i + 1].id, t[i].id, t[i + 1].x - t[i].x);
    }
    sort (t + 1, t + m + 1, cmpy); // 同上,按 y 轴排序
    for (int i = 1; i <= m - 1; ++i) {
        add (t[i].id, t[i + 1].id, t[i + 1].y - t[i].y);
        add (t[i + 1].id, t[i].id, t[i + 1].y - t[i].y);
    }
    for (int i = 1; i <= m; ++i) {
        add (0, t[i].id, min (abs (sx - t[i].x), abs (sy - t[i].y)));
        add (t[i].id, m + 1, abs (tx - t[i].x) + abs (ty - t[i].y));
    }
    add (0, m + 1, abs (sx - tx) + abs (sy - ty));
    dij ();
    return 0;
}
posted @ 2022-03-09 16:54  wangzhongyuan  阅读(26)  评论(0)    收藏  举报  来源