1 条题解
-
0
自动搬运
来自洛谷,原作者为

Heartlessly
AFO搬运于
2025-08-24 21:53:12,当前版本为作者最后更新于2019-05-03 11:29:05,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
Description
给定一个 的网格图,走一条边用时 ,只能在特定的 个点转向,转向用时 ,求从 到 的最短用时。
Solution
考虑 分层图最短路 。
对于样例:
Sample Input
6 9 2 1 2 5 3 2 4 4 5 2 5 6 6 1 6 3 6 4 1 1 4 6Sample Output
27题目所给的图:

我们按照输入的顺序给这些点标上号(包括起点和终点),共 个点。

设 ,表示点的数量。起点为 号节点,终点为 号节点。
我们可以把图分成两层,其中第 层为横向走,第 层为纵向走。
先考虑横向走。将所有点以横坐标为第 关键字,纵坐标为第 关键字从小到大排序。排完序后,判断相邻两个点的横坐标是否相同,如果相同,则在它们两个点之间连一条边权为 两点纵坐标之差的两倍 的双向边。若可以连边 ,则说明从点 可以横向走到点 ,亦可以从点 横向走到点 。
图中可以连的边有:
边权分别为 。
纵向走同理。因为纵向走在第 层,节点编号不能与第 层相同,所以给第 层编号全部 。然后将所有点以纵坐标为第 关键字,横坐标为第 关键字从小到大排序。比较相邻两点纵坐标,连边。若可以连边 ,则说明从点 可以纵向走到点 ,也可以从点 纵向走到点 。
图中可以连的边有:
边权分别为 。
接下来在两层 表示同个节点的点 之间连一条权值为 的双向边,表示可以在该点改变方向,用时为 。
注意起点和终点改变方向不需要 的时间,所以在 起点 与 起点 之间,终点 与 终点 之间连一条边权为 的双向边。
最后跑一遍起点到终点的最短路即是答案,时间复杂度为 。
完整的图:

Code
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <class T> inline void read(T &x) { x = 0; char c = getchar(); bool f = 0; for (; !isdigit(c); c = getchar()) f ^= c == '-'; for (; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); x = f ? -x : x; } template <class T> inline void write(T x) { if (x < 0) { putchar('-'); x = -x; } T y = 1; int len = 1; for (; y <= x / 10; y *= 10) ++len; for (; len; --len, x %= y, y /= 10) putchar(x / y + 48); } const int MAXN = 3e5, MAXM = 3e6; int n, m, s, t, tot, head[MAXN + 5], dis[MAXN + 5]; bool vis[MAXN + 5]; struct Station { int x, y, id; } a[MAXN + 5]; struct Edge { int next, to, dis; } e[MAXM + 5]; struct Node { int val, id; inline friend bool operator<(Node x, Node y) { return x.val > y.val; } }; inline void addEdge(int u, int v, int w) { e[++tot] = (Edge) { head[u], v, w }; head[u] = tot; } inline bool cmpx(Station a, Station b) {//按横坐标排序 return a.x == b.x ? a.y < b.y : a.x < b.x; } inline bool cmpy(Station a, Station b) {//按纵坐标排序 return a.y == b.y ? a.x < b.x : a.y < b.y; } inline void dijkstra(int s) {//堆优化 dijkstra priority_queue<Node> q; memset(dis, 0x3f, sizeof (dis)); dis[s] = 0; q.push((Node) { 0, s }); for (; !q.empty(); ) { int u = q.top().id; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int v, w, i = head[u]; v = e[i].to, w = e[i].dis, i; i = e[i].next) if (dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if (!vis[v]) q.push((Node) { dis[v], v }); } } } int main() { read(n), read(m); n = m + 2;//点的总数 s = n - 1, t = n;//起点 和 终点 for (int i = 1; i <= n; ++i) { read(a[i].x), read(a[i].y); a[i].id = i;//点的编号 } sort(a + 1, a + n + 1, cmpx); for (int i = 1; i < n; ++i) if (a[i].x == a[i + 1].x) { addEdge(a[i].id, a[i + 1].id, (a[i + 1].y - a[i].y) << 1); addEdge(a[i + 1].id, a[i].id, (a[i + 1].y - a[i].y) << 1); } //第一层:横向边 sort(a + 1, a + n + 1, cmpy); for (int i = 1; i < n; ++i) if (a[i].y == a[i + 1].y) { addEdge(a[i].id + n, a[i + 1].id + n, (a[i + 1].x - a[i].x) << 1); addEdge(a[i + 1].id + n, a[i].id + n, (a[i + 1].x - a[i].x) << 1); } //第二层:纵向边 for (int i = 1; i <= n - 2; ++i) addEdge(i, i + n, 1), addEdge(i + n, i, 1); //两层之间:改变方向用时 1 addEdge(s, s + n, 0), addEdge(s + n, s, 0); addEdge(t, t + n, 0), addEdge(t + n, t, 0);//起点和终点改变方向不需要时间 dijkstra(s);//求 s -> t 最短路 write(dis[t] == 0x3f3f3f3f ? -1 : dis[t]);//判断是否有解 putchar('\n'); return 0; }
- 1
信息
- ID
- 2807
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者