diff --git a/other_algorithms/monge_checker.hpp b/other_algorithms/monge_checker.hpp new file mode 100644 index 00000000..fc2a481d --- /dev/null +++ b/other_algorithms/monge_checker.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +// Check whether cost(i, j) is Monge or not +// Complexity: O(nm) +// https://hackmd.io/@tatyam-prime/monge1 +template +std::string check_matrix_monge(auto cost, auto inf, int n, int m) { + if (m < 0) m = n; + + auto Detect = [n, m, inf](auto f) -> std::tuple { + for (int i = 0; i + 1 < n; ++i) { + for (int j = only_upper ? (i + 2) : 0; j + 1 < m; ++j) { + const auto f00 = f(i, j), f01 = f(i, j + 1), f10 = f(i + 1, j), + f11 = f(i + 1, j + 1); + if (f00 >= inf or f01 >= inf) continue; + if (f00 + f11 > f10 + f01) { return {false, i, j}; } + } + } + return {true, -1, -1}; + }; + + if (auto [is_monge, i, j] = Detect(cost); is_monge) { + return "Monge OK"; + } else if (auto [is_anti_monge, ai, aj] = Detect([&cost, inf](int i, int j) { + auto ret = cost(i, j); + return ret == inf ? inf : -ret; + }); + is_anti_monge) { + return "Not Monge, but Anti-Monge OK"; + } else { + std::stringstream ret; + ret << "Not Monge!\n"; + ret << " j=" << std::to_string(j) << " j=" << std::to_string(j + 1) << "\n"; + ret << "i=" << std::to_string(i) << " " << cost(i, j) << " " << cost(i, j + 1) << "\n"; + ret << "i=" << std::to_string(i + 1) << " " << cost(i + 1, j) << " " << cost(i + 1, j + 1); + return ret.str(); + } +} + +// Check whether graph weight is Monge or not +// Complexity: O(n^2) +std::string check_dag_monge(auto cost, auto inf, int n) { + return check_matrix_monge(cost, inf, n, n); +} diff --git a/other_algorithms/monge_checker.md b/other_algorithms/monge_checker.md new file mode 100644 index 00000000..c3614ed6 --- /dev/null +++ b/other_algorithms/monge_checker.md @@ -0,0 +1,37 @@ +--- +title: Monge checker +documentation_of: ./monge_checker.hpp +--- + +与えられた一般の $n \times m$ 行列や $n$ 頂点 DAG の重み行列の Monge 性および Anti-Monge 性を確認し,以下の結果を出力する. + +- Monge であった場合, `Monge OK` と出力 +- Monge ではないが anti-Monge であった場合, `Not Monge, but Anti-Monge OK` と出力 +- Monge でも anti-Monge でもない場合, `Not Monge!` と出力し,続けて Monge 性に反する $2 \times 2$ 小行列を出力 + +計算量は $O(nm)$ や $O(n^2)$ なので,ジャッジへの提出にこの関数の実行を含めると TLE となりうるので注意. + +## 使用方法 + +行列を `std::vector>` 等の形で直接与えるのではなく, `int` 2 つを引数として重みを返す関数 `cost(int, int)` を引数として与えればよい. + +### $n$ 頂点 DAG の辺重み関数の Monge 性判定 + +```cpp +int n; // 頂点: 0, 1, ..., (n - 1) +using T = long long; +T INF; +auto cost = [&](int i, int j) -> T { + // Return weight of directed edge i->j, or INF if the edge does not exist. +}; + +cerr << check_dag_monge(cost, INF, n) << endl; +``` + +## Monge 性の確認方法 + +隣接する 2 行と 2 列の要素からなる全ての $2 \times 2$ 小行列に対する Monge 性を確認すればよい [1]. + +## Links + +- [1] [[Monge まとめ①] Monge 性とは? - HackMD](https://hackmd.io/@tatyam-prime/monge1) diff --git a/other_algorithms/monge_shortest_path.md b/other_algorithms/monge_shortest_path.md index b39e34b8..070c7329 100644 --- a/other_algorithms/monge_shortest_path.md +++ b/other_algorithms/monge_shortest_path.md @@ -40,6 +40,7 @@ Cost ret = monge_shortest_path_with_specified_edges(n, l, r, max_weight, f); - [AtCoder Beginner Contest 218 H - Red and Blue Lamps](https://atcoder.jp/contests/abc218/tasks/abc218_h) - [東京海上日動プログラミングコンテスト2024(AtCoder Beginner Contest 355) G - Baseball](https://atcoder.jp/contests/abc355/tasks/abc355_g) - [東北大学プログラミングコンテスト 2022 K - Lebesgue Integral](https://atcoder.jp/contests/tupc2022/tasks/tupc2022_k) +- [Educational Codeforces Round 181 (Rated for Div. 2) F. Timofey and Docker](https://codeforces.com/contest/2125/problem/F) ## Links