Skip to content

Commit c7dd77b

Browse files
authored
Merge pull request #348 from hitonanode/monge-shortest-path
Monge shortest path
2 parents 6e200e5 + 2003db0 commit c7dd77b

File tree

4 files changed

+181
-0
lines changed

4 files changed

+181
-0
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#pragma once
2+
#include <cassert>
3+
#include <vector>
4+
5+
// Shortest path of Monge-weighted graph
6+
// Variant of LARSCH Algorithm: https://noshi91.hatenablog.com/entry/2023/02/18/005856
7+
// Complexity: O(n log n)
8+
//
9+
// Given a directed graph with n vertices and weighted edges
10+
// (w(i, j) = cost_callback(i, j) (i < j)),
11+
// this class calculates the shortest path from vertex 0 to all other vertices.
12+
template <class Cost> struct monge_shortest_path {
13+
std::vector<Cost> dist; // dist[i] = shortest distance from 0 to i
14+
std::vector<int> amin; // amin[i] = previous vertex of i in the shortest path
15+
16+
template <class F> void _check(int i, int k, F cost_callback) {
17+
if (i <= k) return;
18+
if (Cost c = dist[k] + cost_callback(k, i); c < dist[i]) dist[i] = c, amin[i] = k;
19+
}
20+
21+
template <class F> void _rec_solve(int l, int r, F cost_callback) {
22+
if (r - l == 1) return;
23+
24+
const int m = (l + r) / 2;
25+
for (int k = amin[l]; k <= amin[r]; ++k) _check(m, k, cost_callback);
26+
27+
_rec_solve(l, m, cost_callback);
28+
for (int k = l + 1; k <= m; ++k) _check(r, k, cost_callback);
29+
_rec_solve(m, r, cost_callback);
30+
}
31+
32+
template <class F> Cost solve(int n, F cost_callback) {
33+
assert(n > 0);
34+
dist.resize(n);
35+
amin.assign(n, 0);
36+
37+
dist[0] = Cost();
38+
for (int i = 1; i < n; ++i) dist[i] = cost_callback(0, i);
39+
40+
_rec_solve(0, n - 1, cost_callback);
41+
42+
return dist.back();
43+
}
44+
45+
template <class F> int num_edges() const {
46+
int ret = 0;
47+
for (int c = (int)amin.size() - 1; c >= 0; c = amin[c]) ++ret;
48+
return ret;
49+
}
50+
};
51+
52+
// Find shortest path length from 0 to n - 1 with k edges, min_edges <= k <= max_edges
53+
// https://noshi91.hatenablog.com/entry/2022/01/13/001217
54+
template <class Cost, class F>
55+
Cost monge_shortest_path_with_specified_edges(int n, int min_edges, int max_edges,
56+
Cost max_abs_cost, F cost_callback) {
57+
58+
assert(1 <= n);
59+
assert(0 <= min_edges);
60+
assert(min_edges <= max_edges);
61+
assert(max_edges <= n - 1);
62+
63+
monge_shortest_path<Cost> msp;
64+
65+
auto eval = [&](Cost p) -> Cost {
66+
msp.solve(n, [&](int i, int j) { return cost_callback(i, j) - p; });
67+
return -msp.dist.back() - p * (p < 0 ? max_edges : min_edges);
68+
};
69+
70+
Cost lo = -max_abs_cost * 3, hi = max_abs_cost * 3;
71+
72+
while (lo + 1 < hi) {
73+
Cost p = (lo + hi) / 2, f = eval(p), df = eval(p + 1) - f;
74+
if (df == Cost()) {
75+
return -f;
76+
} else {
77+
(df < Cost() ? lo : hi) = p;
78+
}
79+
}
80+
81+
Cost flo = eval(lo), fhi = eval(hi);
82+
83+
return flo < fhi ? -flo : -fhi;
84+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
title: Shortest path of DAG with Monge weights
3+
documentation_of: ./monge_shortest_path.hpp
4+
---
5+
6+
$n$ 頂点の DAG で辺重みが Monge となるようなものに対して最短路長を高速に求める. [1] で紹介されている簡易版 LARSCH Algorithm が実装されていて,計算量は $O(n \log n)$ .
7+
8+
また,辺数が `min_edges` 以上 `max_edges` 以下であるようなものに限った最短路長を高速に求めることも可能(計算量にさらに重み二分探索の $\log$ がつく).
9+
10+
## 使用方法
11+
12+
### 最短路長の計算
13+
14+
```cpp
15+
auto f = [&](int s, int t) -> Cost {
16+
//
17+
};
18+
19+
monge_shortest_path<Cost> msp;
20+
Cost ret = msp.solve(n, f);
21+
```
22+
23+
### 辺の本数の下限・上限を指定した最短路長の計算
24+
25+
```cpp
26+
auto f = [&](int s, int t) -> Cost {
27+
//
28+
};
29+
30+
int n; // 頂点数
31+
int l, r; // 辺の本数が [l, r] の範囲に収まる最短路を見つけたい
32+
Cost max_weight; // f() が返す値の絶対値の上界
33+
34+
Cost ret = monge_shortest_path_with_specified_edges(n, l, r, max_weight, f);
35+
```
36+
37+
## 問題例
38+
39+
- [No.705 ゴミ拾い Hard - yukicoder](https://yukicoder.me/problems/no/705)
40+
- [AtCoder Beginner Contest 218 H - Red and Blue Lamps](https://atcoder.jp/contests/abc218/tasks/abc218_h)
41+
- [東京海上日動プログラミングコンテスト2024(AtCoder Beginner Contest 355) G - Baseball](https://atcoder.jp/contests/abc355/tasks/abc355_g)
42+
- [東北大学プログラミングコンテスト 2022 K - Lebesgue Integral](https://atcoder.jp/contests/tupc2022/tasks/tupc2022_k)
43+
44+
## Links
45+
46+
- [1] [簡易版 LARSCH Algorithm - noshi91のメモ](https://noshi91.hatenablog.com/entry/2023/02/18/005856)
47+
- [2] [Aliens DP における二分探索の色々 - noshi91のメモ](https://noshi91.hatenablog.com/entry/2023/11/20/052227#fn-c9578a2a)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/min_plus_convolution_convex_arbitrary"
2+
3+
#include "../smawk.hpp"
4+
5+
#include <iostream>
6+
#include <vector>
7+
using namespace std;
8+
9+
int main() {
10+
cin.tie(nullptr);
11+
ios::sync_with_stdio(false);
12+
13+
int N, M;
14+
cin >> N >> M;
15+
vector<int> A(N), B(M);
16+
for (auto &a : A) cin >> a, a = -a;
17+
for (auto &b : B) cin >> b, b = -b;
18+
19+
auto ret = concave_max_plus_convolution<int, ((1 << 30) - 1) * 2>(B, A);
20+
21+
for (int i = 0; i < N + M - 1; ++i) cout << -ret[i] << " \n"[i + 1 == N + M - 1];
22+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#define PROBLEM "https://yukicoder.me/problems/no/705"
2+
#include "../monge_shortest_path.hpp"
3+
4+
#include <cassert>
5+
#include <cmath>
6+
#include <iostream>
7+
using namespace std;
8+
9+
int main() {
10+
cin.tie(nullptr), ios::sync_with_stdio(false);
11+
12+
int N;
13+
cin >> N;
14+
vector<int> A(N), X(N), Y(N);
15+
for (auto &a : A) cin >> a;
16+
for (auto &x : X) cin >> x;
17+
for (auto &y : Y) cin >> y;
18+
19+
auto weight = [&](int j, int i) {
20+
assert(j < i);
21+
--i;
22+
const long long dx = abs(A.at(i) - X.at(j)), dy = Y.at(j);
23+
return dx * dx * dx + dy * dy * dy;
24+
};
25+
26+
monge_shortest_path<long long> msp;
27+
cout << msp.solve(N + 1, weight) << '\n';
28+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy