1/*
2 * ์ต์ ์ ์ฅ ํธ๋ฆฌ (Minimum Spanning Tree)
3 * Kruskal, Prim, Union-Find
4 *
5 * ๊ทธ๋ํ์ ๋ชจ๋ ์ ์ ์ ์ต์ ๋น์ฉ์ผ๋ก ์ฐ๊ฒฐํฉ๋๋ค.
6 */
7
8#include <iostream>
9#include <vector>
10#include <queue>
11#include <algorithm>
12#include <numeric>
13
14using namespace std;
15
16// =============================================================================
17// 1. Union-Find (Disjoint Set Union)
18// =============================================================================
19
20class UnionFind {
21private:
22 vector<int> parent, rank_;
23
24public:
25 UnionFind(int n) : parent(n), rank_(n, 0) {
26 iota(parent.begin(), parent.end(), 0);
27 }
28
29 int find(int x) {
30 if (parent[x] != x) {
31 parent[x] = find(parent[x]); // ๊ฒฝ๋ก ์์ถ
32 }
33 return parent[x];
34 }
35
36 bool unite(int x, int y) {
37 int px = find(x), py = find(y);
38 if (px == py) return false;
39
40 // ๋ญํฌ ๊ธฐ๋ฐ ํฉ์น๊ธฐ
41 if (rank_[px] < rank_[py]) swap(px, py);
42 parent[py] = px;
43 if (rank_[px] == rank_[py]) rank_[px]++;
44
45 return true;
46 }
47
48 bool connected(int x, int y) {
49 return find(x) == find(y);
50 }
51};
52
53// =============================================================================
54// 2. Kruskal ์๊ณ ๋ฆฌ์ฆ
55// =============================================================================
56
57struct Edge {
58 int u, v, weight;
59 bool operator<(const Edge& other) const {
60 return weight < other.weight;
61 }
62};
63
64pair<int, vector<Edge>> kruskal(int n, vector<Edge>& edges) {
65 sort(edges.begin(), edges.end());
66
67 UnionFind uf(n);
68 vector<Edge> mst;
69 int totalWeight = 0;
70
71 for (const auto& e : edges) {
72 if (uf.unite(e.u, e.v)) {
73 mst.push_back(e);
74 totalWeight += e.weight;
75
76 if ((int)mst.size() == n - 1) break;
77 }
78 }
79
80 return {totalWeight, mst};
81}
82
83// =============================================================================
84// 3. Prim ์๊ณ ๋ฆฌ์ฆ
85// =============================================================================
86
87pair<int, vector<pair<int, int>>> prim(int n, const vector<vector<pair<int, int>>>& adj) {
88 vector<bool> visited(n, false);
89 vector<pair<int, int>> mst; // {u, v} ๊ฐ์
90 int totalWeight = 0;
91
92 // {weight, vertex, parent}
93 priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<>> pq;
94 pq.push({0, 0, -1});
95
96 while (!pq.empty() && (int)mst.size() < n) {
97 auto [w, u, parent] = pq.top();
98 pq.pop();
99
100 if (visited[u]) continue;
101 visited[u] = true;
102 totalWeight += w;
103
104 if (parent != -1) {
105 mst.push_back({parent, u});
106 }
107
108 for (auto [v, weight] : adj[u]) {
109 if (!visited[v]) {
110 pq.push({weight, v, u});
111 }
112 }
113 }
114
115 return {totalWeight, mst};
116}
117
118// =============================================================================
119// 4. 2์ฐจ ์ต์ ์ ์ฅ ํธ๋ฆฌ
120// =============================================================================
121
122int secondMST(int n, vector<Edge>& edges) {
123 // ๋จผ์ MST ๊ตฌํ๊ธฐ
124 auto [mstWeight, mst] = kruskal(n, edges);
125
126 int secondBest = INT_MAX;
127
128 // MST์ ๊ฐ ๊ฐ์ ์ ์ ๊ฑฐํ๊ณ ๋ค์ MST ๊ตฌํ๊ธฐ
129 for (int i = 0; i < (int)mst.size(); i++) {
130 vector<Edge> filtered;
131 for (const auto& e : edges) {
132 if (!(e.u == mst[i].u && e.v == mst[i].v && e.weight == mst[i].weight)) {
133 filtered.push_back(e);
134 }
135 }
136
137 auto [newWeight, newMst] = kruskal(n, filtered);
138
139 if ((int)newMst.size() == n - 1) {
140 secondBest = min(secondBest, newWeight);
141 }
142 }
143
144 return secondBest;
145}
146
147// =============================================================================
148// 5. ์ต๋ ์ ์ฅ ํธ๋ฆฌ
149// =============================================================================
150
151pair<int, vector<Edge>> maxSpanningTree(int n, vector<Edge>& edges) {
152 // ๊ฐ์ค์น๋ฅผ ์์๋ก ๋ฐ๊ฟ์ Kruskal ์ ์ฉ
153 for (auto& e : edges) {
154 e.weight = -e.weight;
155 }
156
157 auto [weight, mst] = kruskal(n, edges);
158
159 // ์๋ ๊ฐ์ค์น๋ก ๋ณต์
160 for (auto& e : edges) {
161 e.weight = -e.weight;
162 }
163 for (auto& e : mst) {
164 e.weight = -e.weight;
165 }
166
167 return {-weight, mst};
168}
169
170// =============================================================================
171// 6. ํฌ๋ฌ์ค์ปฌ ์์ฉ: ์ฐ๊ฒฐ ๋น์ฉ ์ต์ํ
172// =============================================================================
173
174int minCostToConnect(int n, vector<vector<int>>& connections) {
175 vector<Edge> edges;
176 for (const auto& conn : connections) {
177 edges.push_back({conn[0] - 1, conn[1] - 1, conn[2]});
178 }
179
180 auto [cost, mst] = kruskal(n, edges);
181
182 if ((int)mst.size() != n - 1) {
183 return -1; // ์ฐ๊ฒฐ ๋ถ๊ฐ
184 }
185
186 return cost;
187}
188
189// =============================================================================
190// 7. Union-Find ์์ฉ: ์น๊ตฌ ๋คํธ์ํฌ
191// =============================================================================
192
193class FriendNetwork {
194private:
195 unordered_map<string, string> parent;
196 unordered_map<string, int> size_;
197
198 string find(const string& x) {
199 if (parent.find(x) == parent.end()) {
200 parent[x] = x;
201 size_[x] = 1;
202 }
203 if (parent[x] != x) {
204 parent[x] = find(parent[x]);
205 }
206 return parent[x];
207 }
208
209public:
210 int unite(const string& a, const string& b) {
211 string pa = find(a), pb = find(b);
212
213 if (pa == pb) {
214 return size_[pa];
215 }
216
217 if (size_[pa] < size_[pb]) swap(pa, pb);
218 parent[pb] = pa;
219 size_[pa] += size_[pb];
220
221 return size_[pa];
222 }
223};
224
225// =============================================================================
226// 8. Union-Find ์์ฉ: ์ค๋ณต ์ฐ๊ฒฐ ์ฐพ๊ธฐ
227// =============================================================================
228
229vector<int> findRedundantConnection(vector<vector<int>>& edges) {
230 int n = edges.size();
231 UnionFind uf(n + 1);
232
233 for (const auto& e : edges) {
234 if (!uf.unite(e[0], e[1])) {
235 return e; // ์ค๋ณต ์ฐ๊ฒฐ
236 }
237 }
238
239 return {};
240}
241
242// =============================================================================
243// ํ
์คํธ
244// =============================================================================
245
246int main() {
247 cout << "============================================================" << endl;
248 cout << "์ต์ ์ ์ฅ ํธ๋ฆฌ ์์ " << endl;
249 cout << "============================================================" << endl;
250
251 // ํ
์คํธ ๊ทธ๋ํ
252 // 1 --(4)-- 2
253 // /| /|
254 // (1)| (3)|
255 // / | / |
256 // 0 (2) / (5)
257 // \ | / |
258 // (3)| / |
259 // \|/ |
260 // 3 --(6)-- 4
261
262 int n = 5;
263 vector<Edge> edges = {
264 {0, 1, 1}, {0, 3, 3}, {1, 2, 4}, {1, 3, 2},
265 {2, 3, 3}, {2, 4, 5}, {3, 4, 6}
266 };
267
268 // 1. Kruskal
269 cout << "\n[1] Kruskal ์๊ณ ๋ฆฌ์ฆ" << endl;
270 auto [kWeight, kMst] = kruskal(n, edges);
271 cout << " MST ๊ฐ์ค์น: " << kWeight << endl;
272 cout << " MST ๊ฐ์ : ";
273 for (const auto& e : kMst) {
274 cout << "(" << e.u << "-" << e.v << ":" << e.weight << ") ";
275 }
276 cout << endl;
277
278 // 2. Prim
279 cout << "\n[2] Prim ์๊ณ ๋ฆฌ์ฆ" << endl;
280 vector<vector<pair<int, int>>> adj(n);
281 for (const auto& e : edges) {
282 adj[e.u].push_back({e.v, e.weight});
283 adj[e.v].push_back({e.u, e.weight});
284 }
285 auto [pWeight, pMst] = prim(n, adj);
286 cout << " MST ๊ฐ์ค์น: " << pWeight << endl;
287 cout << " MST ๊ฐ์ : ";
288 for (const auto& [u, v] : pMst) {
289 cout << "(" << u << "-" << v << ") ";
290 }
291 cout << endl;
292
293 // 3. Union-Find
294 cout << "\n[3] Union-Find" << endl;
295 UnionFind uf(5);
296 uf.unite(0, 1);
297 uf.unite(2, 3);
298 uf.unite(1, 2);
299 cout << " ํฉ์น ํ: 0-1, 2-3, 1-2" << endl;
300 cout << " 0๊ณผ 3 ์ฐ๊ฒฐ๋จ: " << (uf.connected(0, 3) ? "์" : "์๋์ค") << endl;
301 cout << " 0๊ณผ 4 ์ฐ๊ฒฐ๋จ: " << (uf.connected(0, 4) ? "์" : "์๋์ค") << endl;
302
303 // 4. 2์ฐจ MST
304 cout << "\n[4] 2์ฐจ ์ต์ ์ ์ฅ ํธ๋ฆฌ" << endl;
305 vector<Edge> edges2 = edges; // ๋ณต์ฌ
306 int secondWeight = secondMST(n, edges2);
307 cout << " 2์ฐจ MST ๊ฐ์ค์น: " << secondWeight << endl;
308
309 // 5. ์ต๋ ์ ์ฅ ํธ๋ฆฌ
310 cout << "\n[5] ์ต๋ ์ ์ฅ ํธ๋ฆฌ" << endl;
311 vector<Edge> edges3 = edges; // ๋ณต์ฌ
312 auto [maxWeight, maxMst] = maxSpanningTree(n, edges3);
313 cout << " Max ST ๊ฐ์ค์น: " << maxWeight << endl;
314
315 // 6. ์ค๋ณต ์ฐ๊ฒฐ
316 cout << "\n[6] ์ค๋ณต ์ฐ๊ฒฐ ์ฐพ๊ธฐ" << endl;
317 vector<vector<int>> redEdges = {{1, 2}, {1, 3}, {2, 3}};
318 auto redundant = findRedundantConnection(redEdges);
319 cout << " ๊ฐ์ : (1,2), (1,3), (2,3)" << endl;
320 cout << " ์ค๋ณต: (" << redundant[0] << ", " << redundant[1] << ")" << endl;
321
322 // 7. ๋ณต์ก๋ ์์ฝ
323 cout << "\n[7] ๋ณต์ก๋ ์์ฝ" << endl;
324 cout << " | ์๊ณ ๋ฆฌ์ฆ | ์๊ฐ๋ณต์ก๋ | ํน์ง |" << endl;
325 cout << " |-------------|------------------|-------------------|" << endl;
326 cout << " | Kruskal | O(E log E) | ๊ฐ์ ๊ธฐ๋ฐ, ํฌ์ |" << endl;
327 cout << " | Prim | O((V+E) log V) | ์ ์ ๊ธฐ๋ฐ, ๋ฐ์ง |" << endl;
328 cout << " | Union-Find | O(ฮฑ(n)) โ O(1) | ๊ฑฐ์ ์์ ์๊ฐ |" << endl;
329
330 cout << "\n============================================================" << endl;
331
332 return 0;
333}