P9813 Convex Hull 题解

IfyoungY Lv1

题面 以及 视图

别样的阅读体验 / 别样的阅读体验

题目内容说的很直白了,接下来讲解做法。

思路

可以发现这是一个最短路问题,在最基础的模板上增加了一个“路径上边的 之和 ”的限制。

为方便讲解,我们将“路径上边的 之和”用一个字母 代替。

考虑仍然使用 Dijkstra 算法(堆优化),并且进行一些细微的改动。

首先, 数组要增加一维,即由 变成 ,同时,其定义变成 从起点到节点 , 恰好为 的最短路径。

下面考虑对于算法实现过程的改动,具体表现为 更新 时的改动。

考虑当前点的“答案”,即当前点的 为某个小于 的值时距离起点的距离,是如何得到的。

可以类比一下 背包 的思想,会发现当前点的“答案”是通过相邻节点 对应位置 上的答案加上两点之间的距离转移来的。

那么什么是“对应位置”呢?

这个“位置”指的是下标,两点之间的下标应相差一个相应的 。

下面是形式化一些的表达:

假设当前的节点为 ,遍历到的相邻节点为 ,那么 更新 的方程为 ,其中 ( 不能小于 ,同时 要小于 )。

直观地讲就是 下标 增加了一个 ,值 增加了一个 。(不难理解了吧……)

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <bits/stdc++.h>

using namespace std;

typedef pair <int, int> PII;

const int K = 210, N = 2e3 + 5, M = 1e4 + 5; // 数据范围

int k, n, m, s, t;
int he[N], tot;
struct edge {
int to, nxt, t, h;
} e[M << 1]; // 双向边
int dis[N][K];
bool vis[N];
int ans = 0x3f3f3f3f; // 初始化成最大值,最终找到最小值

void add(int u, int v, int t, int h) {
e[ ++ tot] = (edge) {v, he[u], t, h};
he[u] = tot;
}

void dijkstra(int s) {
memset(dis, 0x3f, sizeof dis); // 类似于普通的 Dijkstra 中的初始化
dis[s][0] = 0;
priority_queue <PII, vector <PII>, greater <PII> > q; // 小根堆
q.push({0, s});
while (q.size()) {
auto p = q.top();
q.pop();
int u = p.second;
for (int i = he[u]; i; i = e[i].nxt) {
int v = e[i].to, h = e[i].h, t = e[i].t;
int minn = 0x3f3f3f3f; // 要取一个“最小值”
for (int j = 0; j < k - h; j ++ ) { // 循环区间合法
if (dis[v][j + h] > dis[u][j] + t) {
dis[v][j + h] = dis[u][j] + t;
minn = min(minn, dis[v][j + h]); // 要在更新后的数据中找到“最小值”,以进行接下来的更新
}
}
if (minn != 0x3f3f3f3f) // 如果“更新成功”
q.push({minn, v}); // 那么就把“最小值”放进堆里,进行接下来的更新
}
}
}

int main() {
cin >> k >> n >> m;
for (int i = 1, u, v, t, h; i <= m; i ++ ) {
cin >> u >> v >> t >> h;
add(u, v, t, h), add(v, u, t, h); // 建双向边
}
cin >> s >> t;
dijkstra(s); // 从起点开始
for (int i = 0; i < k; i ++ ) ans = min(ans, dis[t][i]); // 在 p 所有小于 k 的答案中找最小值
if (ans >= 0x3f3f3f3f / 2) // 一种技巧,即起点并不能通向终点,但终点会被其旁边的点更新
puts("-1"); // 但是又不会变小太多,所以用正无穷的一半即可完成判定
else cout << ans << endl;
return 0;
}
  • 标题: P9813 Convex Hull 题解
  • 作者: IfyoungY
  • 创建于 : 2023-10-26 14:04:36
  • 更新于 : 2023-10-26 14:20:15
  • 链接: https://redefine.ohevan.com/2023/10/26/P9813-Convex-Hull-题解/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
 评论
此页目录
P9813 Convex Hull 题解