From 8246e0b1c1737f6f113cedd476874cfa5eb37d67 Mon Sep 17 00:00:00 2001 From: kzrnm Date: Wed, 20 Dec 2023 19:50:36 +0900 Subject: [PATCH] Minimum Spanning Tree --- graph/minimum_spanning_tree/checker.cpp | 99 +++++++++++++++++++ graph/minimum_spanning_tree/gen/example_00.in | 8 ++ graph/minimum_spanning_tree/gen/example_01.in | 4 + graph/minimum_spanning_tree/gen/example_02.in | 6 ++ .../minimum_spanning_tree/gen/max_random.cpp | 41 ++++++++ graph/minimum_spanning_tree/gen/random.cpp | 41 ++++++++ graph/minimum_spanning_tree/gen/small.cpp | 43 ++++++++ graph/minimum_spanning_tree/gen/small_c01.cpp | 43 ++++++++ graph/minimum_spanning_tree/gen/star.cpp | 34 +++++++ graph/minimum_spanning_tree/hash.json | 50 ++++++++++ graph/minimum_spanning_tree/info.toml | 26 +++++ graph/minimum_spanning_tree/sol/correct.cpp | 93 +++++++++++++++++ graph/minimum_spanning_tree/task.md | 57 +++++++++++ graph/minimum_spanning_tree/verifier.cpp | 70 +++++++++++++ 14 files changed, 615 insertions(+) create mode 100644 graph/minimum_spanning_tree/checker.cpp create mode 100644 graph/minimum_spanning_tree/gen/example_00.in create mode 100644 graph/minimum_spanning_tree/gen/example_01.in create mode 100644 graph/minimum_spanning_tree/gen/example_02.in create mode 100644 graph/minimum_spanning_tree/gen/max_random.cpp create mode 100644 graph/minimum_spanning_tree/gen/random.cpp create mode 100644 graph/minimum_spanning_tree/gen/small.cpp create mode 100644 graph/minimum_spanning_tree/gen/small_c01.cpp create mode 100644 graph/minimum_spanning_tree/gen/star.cpp create mode 100644 graph/minimum_spanning_tree/hash.json create mode 100644 graph/minimum_spanning_tree/info.toml create mode 100644 graph/minimum_spanning_tree/sol/correct.cpp create mode 100644 graph/minimum_spanning_tree/task.md create mode 100644 graph/minimum_spanning_tree/verifier.cpp diff --git a/graph/minimum_spanning_tree/checker.cpp b/graph/minimum_spanning_tree/checker.cpp new file mode 100644 index 000000000..6e28f4123 --- /dev/null +++ b/graph/minimum_spanning_tree/checker.cpp @@ -0,0 +1,99 @@ +#include "testlib.h" +#include +#include +#include + +using namespace std; +using ll = long long; + +struct E +{ + int a, b; + ll dist; +}; + +struct UnionFind +{ + vector p, r; + int gn; + UnionFind(int n = 0) : p(n, -1), r(n, 1), gn(n) {} + void merge(int a, int b) + { + int x = group(a), y = group(b); + if (x == y) + return; // same + gn--; + if (r[x] < r[y]) + { + p[x] = y; + } + else + { + p[y] = x; + if (r[x] == r[y]) + r[x]++; + } + } + int group(int a) + { + if (p[a] == -1) + return a; + return p[a] = group(p[a]); + } + bool same(int a, int b) { return group(a) == group(b); } +}; + +ll read_ans(int n, int m, vector edges, InStream &stream) +{ + ll x = stream.readLong(); + ll sum = 0; + UnionFind uf(n); + for (int i = 0; i < n - 1; i++) + { + int ei = stream.readInt(0, m); + auto e = edges[ei]; + if (uf.same(e.a, e.b)) + { + stream.quit(_wa, "input isn't tree"); + } + uf.merge(e.a, e.b); + sum += e.dist; + } + + if (x != sum) + { + stream.quitf(_wa, "X(" I64 ") isn't correct, sum = " I64, x, sum); + } + return x; +} + +int main(int argc, char *argv[]) +{ + setName("compare mst"); + registerTestlibCmd(argc, argv); + + // input + int n = inf.readInt(); + int m = inf.readInt(); + vector edges(m); + for (int i = 0; i < m; i++) + { + int a = inf.readInt(); + int b = inf.readInt(); + ll c = inf.readLong(); + edges[i] = {a, b, c}; + } + + ll x_ans = read_ans(n, m, edges, ans); + ll x_ouf = read_ans(n, m, edges, ouf); + + if (x_ans < x_ouf) + { + quitf(_wa, "There is the better solution"); + } + if (x_ans > x_ouf) + { + quitf(_fail, "Participate find better answer"); + } + quitf(_ok, "OK: " I64, x_ouf); +} diff --git a/graph/minimum_spanning_tree/gen/example_00.in b/graph/minimum_spanning_tree/gen/example_00.in new file mode 100644 index 000000000..0b3173436 --- /dev/null +++ b/graph/minimum_spanning_tree/gen/example_00.in @@ -0,0 +1,8 @@ +4 7 +0 1 4 +0 2 2 +0 3 3 +1 2 6 +1 3 8 +2 3 1 +1 1 0 diff --git a/graph/minimum_spanning_tree/gen/example_01.in b/graph/minimum_spanning_tree/gen/example_01.in new file mode 100644 index 000000000..0bc7e5fd8 --- /dev/null +++ b/graph/minimum_spanning_tree/gen/example_01.in @@ -0,0 +1,4 @@ +4 3 +0 1 1 +1 2 2 +3 1 3 diff --git a/graph/minimum_spanning_tree/gen/example_02.in b/graph/minimum_spanning_tree/gen/example_02.in new file mode 100644 index 000000000..1b4dec93e --- /dev/null +++ b/graph/minimum_spanning_tree/gen/example_02.in @@ -0,0 +1,6 @@ +6 5 +0 1 1000000000 +1 2 1000000000 +2 3 1000000000 +3 4 1000000000 +4 5 1000000000 diff --git a/graph/minimum_spanning_tree/gen/max_random.cpp b/graph/minimum_spanning_tree/gen/max_random.cpp new file mode 100644 index 000000000..1f64a0c7a --- /dev/null +++ b/graph/minimum_spanning_tree/gen/max_random.cpp @@ -0,0 +1,41 @@ +#include +#include "random.h" +#include "../params.h" + +using namespace std; + +struct E +{ + int from, to, cost; +}; + +int main(int, char *argv[]) +{ + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = gen.uniform(N_MAX - 100, N_MAX); + int m = gen.uniform(n - 1, (int)N_MAX); + + vector edges; + for (int i = 1; i < n; i++) + { + int c = gen.uniform(0LL, C_MAX); + edges.push_back({gen.uniform(0, i - 1), i, c}); + } + for (int i = n - 1; i < m; i++) + { + int a = gen.uniform(0, n - 1); + int b = gen.uniform(0, n - 1); + int c = gen.uniform(0LL, C_MAX); + edges.push_back({a, b, c}); + } + + auto idx = gen.perm(n); + printf("%d %d\n", n, m); + for (auto e : edges) + { + printf("%d %d %d\n", idx[e.from], idx[e.to], e.cost); + } + return 0; +} \ No newline at end of file diff --git a/graph/minimum_spanning_tree/gen/random.cpp b/graph/minimum_spanning_tree/gen/random.cpp new file mode 100644 index 000000000..bdeaeda9c --- /dev/null +++ b/graph/minimum_spanning_tree/gen/random.cpp @@ -0,0 +1,41 @@ +#include +#include "random.h" +#include "../params.h" + +using namespace std; + +struct E +{ + int from, to, cost; +}; + +int main(int, char *argv[]) +{ + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = gen.uniform(1LL, N_MAX); + int m = gen.uniform(n - 1, (int)N_MAX); + + vector edges; + for (int i = 1; i < n; i++) + { + int c = gen.uniform(0LL, C_MAX); + edges.push_back({gen.uniform(0, i - 1), i, c}); + } + for (int i = n - 1; i < m; i++) + { + int a = gen.uniform(0, n - 1); + int b = gen.uniform(0, n - 1); + int c = gen.uniform(0LL, C_MAX); + edges.push_back({a, b, c}); + } + + auto idx = gen.perm(n); + printf("%d %d\n", n, m); + for (auto e : edges) + { + printf("%d %d %d\n", idx[e.from], idx[e.to], e.cost); + } + return 0; +} \ No newline at end of file diff --git a/graph/minimum_spanning_tree/gen/small.cpp b/graph/minimum_spanning_tree/gen/small.cpp new file mode 100644 index 000000000..2b4335c80 --- /dev/null +++ b/graph/minimum_spanning_tree/gen/small.cpp @@ -0,0 +1,43 @@ +#include +#include "random.h" +#include "../params.h" + +#define SMALL_N_MAX 1000 + +using namespace std; + +struct E +{ + int from, to, cost; +}; + +int main(int, char *argv[]) +{ + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = gen.uniform(1, SMALL_N_MAX); + int m = gen.uniform(n - 1, (int)N_MAX); + + vector edges; + for (int i = 1; i < n; i++) + { + int c = gen.uniform(0LL, C_MAX); + edges.push_back({gen.uniform(0, i - 1), i, c}); + } + for (int i = n - 1; i < m; i++) + { + int a = gen.uniform(0, n - 1); + int b = gen.uniform(0, n - 1); + int c = gen.uniform(0LL, C_MAX); + edges.push_back({a, b, c}); + } + + auto idx = gen.perm(n); + printf("%d %d\n", n, m); + for (auto e : edges) + { + printf("%d %d %d\n", idx[e.from], idx[e.to], e.cost); + } + return 0; +} \ No newline at end of file diff --git a/graph/minimum_spanning_tree/gen/small_c01.cpp b/graph/minimum_spanning_tree/gen/small_c01.cpp new file mode 100644 index 000000000..4ab856842 --- /dev/null +++ b/graph/minimum_spanning_tree/gen/small_c01.cpp @@ -0,0 +1,43 @@ +#include +#include "random.h" +#include "../params.h" + +#define SMALL_N_MAX 1000 + +using namespace std; + +struct E +{ + int from, to, cost; +}; + +int main(int, char *argv[]) +{ + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = gen.uniform(1, SMALL_N_MAX); + int m = gen.uniform(n - 1, 2 * n); + + vector edges; + for (int i = 1; i < n; i++) + { + int c = gen.uniform(0, 1); + edges.push_back({gen.uniform(0, i - 1), i, c}); + } + for (int i = n - 1; i < m; i++) + { + int a = gen.uniform(0, n - 1); + int b = gen.uniform(0, n - 1); + int c = gen.uniform(0, 1); + edges.push_back({a, b, c}); + } + + auto idx = gen.perm(n); + printf("%d %d\n", n, m); + for (auto e : edges) + { + printf("%d %d %d\n", idx[e.from], idx[e.to], e.cost); + } + return 0; +} \ No newline at end of file diff --git a/graph/minimum_spanning_tree/gen/star.cpp b/graph/minimum_spanning_tree/gen/star.cpp new file mode 100644 index 000000000..fc3e4238b --- /dev/null +++ b/graph/minimum_spanning_tree/gen/star.cpp @@ -0,0 +1,34 @@ +#include +#include "random.h" +#include "../params.h" + +using namespace std; + +struct E +{ + int from, to, cost; +}; + +int main(int, char *argv[]) +{ + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = N_MAX; + int m = n - 1; + + vector edges; + for (int i = 1; i < n; i++) + { + int c = gen.uniform(0LL, C_MAX); + edges.push_back({0, i, c}); + } + + vector idx = gen.perm(n); + printf("%d %d\n", n, m); + for (auto e : edges) + { + printf("%d %d %d\n", idx[e.from], idx[e.to], e.cost); + } + return 0; +} \ No newline at end of file diff --git a/graph/minimum_spanning_tree/hash.json b/graph/minimum_spanning_tree/hash.json new file mode 100644 index 000000000..12ab9c9f1 --- /dev/null +++ b/graph/minimum_spanning_tree/hash.json @@ -0,0 +1,50 @@ +{ + "example_00.in": "020c548c74d1ad61da527d9dd149e5510cba1a30459fe3f933b6ec0b742f87d0", + "example_00.out": "ad3c92a1dec31cb7cb05f6f2dfd5c7a1517f73b86a3c7cc1b131a17c0c01dd84", + "example_01.in": "4859c57ba64d8088c41b77c192a9535ac0682e0e478848d9a8fd238a2c4b7691", + "example_01.out": "fe8b5f684b0ffe72aab5b90254f0e42855029fd343c56e3104a70847446f8fbf", + "example_02.in": "026aafd663e40b9bbc108768553e0b89a3bf8130e4bdc46c29296f864c5e41bb", + "example_02.out": "4906a5c6228e0dec625c797d8d5364f4871b8f067dd9a45314123b4c95e98897", + "max_random_00.in": "c58ac9ee4e3a587a14ed8281e3e0caa8181fefb559848b6b40fcaf1908226aef", + "max_random_00.out": "99bf4f81049f93f4aec70180976be2e0b2111c280f1b8a2d358802bdc22b9de6", + "max_random_01.in": "7923205b114a3b91eeffa3b34ffbf3dd2457f7514876c4919c92fe8af944d4f2", + "max_random_01.out": "b1f78ec766ae00aaae3d7fffff4a6aca2b758cd86bca29d31569cc4527fb2cd5", + "max_random_02.in": "e922cddc347c430132015acd94835261383f14d436d16376f68b78903d06b975", + "max_random_02.out": "791ced302a13a1f34305bca903396001a449d8e96700812cd8c99f28e2081ae7", + "max_random_03.in": "dfef6713c2ac6309f9cd4a1fe5f39e0ba5d13f98731891d25ca8a8c5d55a6cd9", + "max_random_03.out": "9cd10fef97af5b075e6a3f5205e010401b9992d450e9f07afca154109ddd9c78", + "max_random_04.in": "38a30545074ddfa969bc382751d6502f71f00a5058a8a76238196051b5cc555f", + "max_random_04.out": "8bddd5ae832235f7784ba084795ca58d623cedfd1ee8cd5f65358b63486885d4", + "random_00.in": "a0f03f21f6e3a29c879eea983c3473c3afa70f7f5430b5bf2a18539af7ef699c", + "random_00.out": "3db97134e106cf943c0b2ff212dd146ba47781af727b387ccd6222ce2ee41b40", + "random_01.in": "9b6c3e3c0d9159d31eb122334e1d06a4613c49c64486b035ae9e12026a337ff8", + "random_01.out": "eec284432e5df4149d02ed8e9ee23db8184ae8c74a345cb4cda5f8e3800049f1", + "random_02.in": "8f14df85adeec8b34d1b36fc72964f913ae71e3adc1fbb5ad6c34fe3ea292099", + "random_02.out": "463eebcf2e0e49e932744789799f96f1a11ffad6066fb95caa38b42c4ff10c18", + "random_03.in": "6f64c9c91034c30f948b9e5ef46c9fffe8180b4da26153d914020324279af08b", + "random_03.out": "98d9cb7fbe298fe8f37ea870038554c56e042ee8fd3acb97fe88caa4ef7f14b3", + "random_04.in": "fc3fa49fbcc1b2ad3cf9e8d022a43b5dc6bae05e7ead475b461633e0de8c1824", + "random_04.out": "7cec49b3db09000cc8b9766405a6dfcdf69c6f219c7cfca6dcc8bd0098e1aa1d", + "small_00.in": "4e40f0736cda259a70522b2426b2a204466eefcfa8fcb0851af8958e2722f2fe", + "small_00.out": "78cc7cd0c89413f9508b2f2e7505633ed77d5e759b1485c90f2e191a618846ab", + "small_01.in": "d8cce13331750d77510a5cbe326615a7634d03770aa24d508cdc19dbda581319", + "small_01.out": "1296881c571229c44b864c0329a9bd2bbfc71e799ce224311a6caba3ba3771db", + "small_02.in": "4eb698b18026e57cb664db73a563368b7f9aeba5e80dc9971481c67814a16f85", + "small_02.out": "8edb40464d90b4bf3b3fd7ad346b9d2084ede859e648b5610d86d5916d80b7a1", + "small_03.in": "709461c35d8545f3dd6f16f794aa7ec4d7c2bb6b0fda4e4c45fa3eb365754c49", + "small_03.out": "06ec70360e6f935a5d30ebac62f623f305015949556f48327d3b56c6bc39fce7", + "small_04.in": "1b6de416fb67cba7028997613ec9f582eb232932435673343dae01f431859d84", + "small_04.out": "c8f1bc9ec7bc4617c8604fde9be53bb4b103aa93391354b10cabef8a21a3ddc4", + "small_c01_00.in": "d6ae2e7f8aab2edb91bd490c49f78935a8de13bdd6a98706535aeb404488f5f4", + "small_c01_00.out": "7d47fefe4b27651468b72f4d2bb3da842048fe76b9d65c1d8802070a1349692e", + "small_c01_01.in": "adba72e6de177bfb7b20342c7ee7ba64d59a56038d8c28db221d6d68cf6f0160", + "small_c01_01.out": "765332438f2fe6735bd0953b4bef824f1790668f56146dfbd2a0e834ddac8561", + "small_c01_02.in": "c9ce0284afdc5d36aa3f6db85e2d1181c577257f1a4d66f939b2caa7f0180a2a", + "small_c01_02.out": "adb7545034f87254b0f878e990c1688360a525f3df1717718556cc1d351a59e5", + "small_c01_03.in": "b2d2764bb9f05bdda5ee45c40685bae660dcca23a673ba1290c43387dc449c64", + "small_c01_03.out": "61062983b3098eb5d89e7321187b0a6ae43d12bbb6d12aa9c4e8f4daf7266af7", + "small_c01_04.in": "aece442481b2b2fd40bcaaf5373afbb575c933f71abfd8b0235d84aeead3398f", + "small_c01_04.out": "7d05b511a7073be10f4d139433f2fb7099bb39c4add8b4a960ef824ce282be7f", + "star_00.in": "2b04764631aa1eaa5d7a46373025f7fd0993db946bef83b6d0499942878330b5", + "star_00.out": "31cefbd15822eeeffcd67b793deeafa522c4783a0c2b4bcb09a8ff79e563d6bf" +} \ No newline at end of file diff --git a/graph/minimum_spanning_tree/info.toml b/graph/minimum_spanning_tree/info.toml new file mode 100644 index 000000000..278a75774 --- /dev/null +++ b/graph/minimum_spanning_tree/info.toml @@ -0,0 +1,26 @@ +title = 'Minimum Spanning Tree' +timelimit = 5.0 +forum = "https://github.com/yosupo06/library-checker-problems/issues/953" + +[[tests]] + name = "example.in" + number = 3 +[[tests]] + name = "random.cpp" + number = 5 +[[tests]] + name = "max_random.cpp" + number = 5 +[[tests]] + name = "small.cpp" + number = 5 +[[tests]] + name = "small_c01.cpp" + number = 5 +[[tests]] + name = "star.cpp" + number = 1 + +[params] + N_MAX = 500_000 + C_MAX = 1_000_000_000 diff --git a/graph/minimum_spanning_tree/sol/correct.cpp b/graph/minimum_spanning_tree/sol/correct.cpp new file mode 100644 index 000000000..a2ca0c3ee --- /dev/null +++ b/graph/minimum_spanning_tree/sol/correct.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include + +using namespace std; +using ll = long long; +template +using V = vector; +template +using VV = V>; + +struct E +{ + int index, to; + ll dist; +}; + +/** +Information of mst +*/ +struct MstInfo +{ + ll cost; /// mst cost + V res; /// edge list +}; + +/// calc mst by Prim's algorithm +MstInfo prim(const VV &g, const int start) +{ + int n = int(g.size()); + V vis(n); + auto comp = [&](E a, E b) + { + return a.dist > b.dist; + }; + priority_queue, decltype(comp)> pq{comp}; + + vis[start] = true; + for (auto &e : g[start]) + { + pq.push(e); + } + ll cost = 0; + V res; + while (pq.size()) + { + auto e = pq.top(); + pq.pop(); + if (vis[e.to]) + { + continue; + } + vis[e.to] = true; + cost += e.dist; + res.push_back(e.index); + for (auto &e2 : g[e.to]) + { + if (!vis[e2.to]) + pq.push(e2); + } + } + return {cost, res}; +} + +int main() +{ + int n, m; + scanf("%d %d", &n, &m); + + VV g(n); + for (int i = 0; i < m; i++) + { + int a, b; + ll c; + scanf("%d %d %lld", &a, &b, &c); + g[a].push_back({i, b, c}); + g[b].push_back({i, a, c}); + } + + auto [cost, res] = prim(g, 0); + + printf("%lld\n", cost); + for (int i = 0; i < n - 1; i++) + { + if (i != 0) + printf(" "); + printf("%d", res[i]); + } + printf("\n"); + + return 0; +} diff --git a/graph/minimum_spanning_tree/task.md b/graph/minimum_spanning_tree/task.md new file mode 100644 index 000000000..71a3ed4de --- /dev/null +++ b/graph/minimum_spanning_tree/task.md @@ -0,0 +1,57 @@ +## @{keyword.statement} + +@{lang.en} + +Given a weighted undirected graph with $N$ vertices and $M$ edges. $i$-th edge is $(a_i, b_i)$ and has a weight of $c_i$. This graph may not be simple. +Find the minimum spanning tree. + +@{lang.ja} + +$N$ 頂点 $M$ 辺の無向グラフが与えられる。$i$ 番目の辺は $(a_i, b_i)$ であり、重さ $c_i$ である。このグラフは単純とは限らない。 +最小全域木を求めよ。 + +@{lang.end} + +## @{keyword.constraints} + +- $1 \leq N \leq @{param.N_MAX}$ +- $N - 1 \leq M \leq @{param.N_MAX}$ +- $0 \leq a_i, b_i < N$ +- $0 \leq c_i \leq @{param.C_MAX}$ +- @{lang.en} The given graph is connected. @{lang.ja} 与えられるグラフは連結 @{lang.end} + +## @{keyword.input} + +~~~ +$N$ $M$ +$a_0$ $b_0$ $c_0$ +$a_1$ $b_1$ $c_1$ +: +$a_{M - 1}$ $b_{M - 1}$ $c_{M - 1}$ +~~~ + +## @{keyword.output} + +~~~ +$X$ +$e_0$ $e_1$ $e_2$ ... $e_{N - 1}$ +~~~ + +@{lang.en} +$X$ is the sum of the weights of the edges in the minimum spanning tree. $e_i$ is the index of the edge in the minimum spanning tree. +If there are multiple correct output, print any of them. + +@{lang.ja} + +ただし、$X$ は木の重みの総和であり、$e_i$ は最小全域木に含まれる辺の番号である。 +解が複数存在する場合、どれを返しても構わない。 + +@{lang.end} + +## @{keyword.sample} + +@{example.example_00} + +@{example.example_01} + +@{example.example_02} diff --git a/graph/minimum_spanning_tree/verifier.cpp b/graph/minimum_spanning_tree/verifier.cpp new file mode 100644 index 000000000..6e7ea63e1 --- /dev/null +++ b/graph/minimum_spanning_tree/verifier.cpp @@ -0,0 +1,70 @@ +#include +#include +#include +#include "testlib.h" +#include "params.h" +#include + +using namespace std; + +struct UnionFind +{ + vector p, r; + int gn; + UnionFind(int n = 0) : p(n, -1), r(n, 1), gn(n) {} + void merge(int a, int b) + { + int x = group(a), y = group(b); + if (x == y) + return; // same + gn--; + if (r[x] < r[y]) + { + p[x] = y; + } + else + { + p[y] = x; + if (r[x] == r[y]) + r[x]++; + } + } + int group(int a) + { + if (p[a] == -1) + return a; + return p[a] = group(p[a]); + } + bool same(int a, int b) { return group(a) == group(b); } + int groupCount() + { + return gn; + } +}; + +int main() +{ + registerValidation(); + + int n = inf.readInt(1, N_MAX); + inf.readSpace(); + int m = inf.readInt(n - 1, N_MAX); + inf.readChar('\n'); + + UnionFind uf(n); + for (int i = 0; i < m; i++) + { + int a = inf.readInt(0, n - 1); + inf.readSpace(); + int b = inf.readInt(0, n - 1); + inf.readSpace(); + inf.readInt(0, C_MAX); + inf.readChar('\n'); + + uf.merge(a, b); + } + inf.readEof(); + + ensure(uf.groupCount() == 1); + return 0; +}