# 題目: UVa 11631 - Dark Roads

# 題目說明

一個城市裡有很多的路相連,道路上每一公尺,路燈的 cost 為 1
找出每個路口能到任意其他路口,最多能減少多少 cost 的路燈
(整條路每一公尺有一個路燈)


INPUT:
每筆測資第一行有兩個整數 mn

  1. m 代表路口的數量
  2. n 代表道路的數量

接下來有 n 行,每行有三個整數 xyz
代表 xy 路口間有一條長 z 的路


OUTPUT:
輸出最多能減少多少 cost 的路燈

# 解題方法

先將所有路口的編號及 cost 存入 edge
接著以 kruskcal 演算法,先對距離進行 sort ,再用 union_find 演算法判斷是否可連通
以總 cost,每次減掉最短路徑的 cost,即為答案

# 參考程式碼

#include <iostream>
#include <vector>
#include <tuple>
#include <algorithm>
using namespace std;
vector< tuple<int, int, int> > edge;
vector<int> parents;
vector<int> ranks;
int cost;
bool cmp(tuple<int, int, int> r1, tuple<int, int, int> r2)
{
	return get<2>(r1) < get<2>(r2);
}
bool union_find(int r1, int r2)
{
	// find root of r1 and r2
	while (r1 != parents[r1]) r1 = parents[r1];
	while (r2 != parents[r2]) r2 = parents[r2];
	// circle
	if (r1 == r2) return false;
	if (ranks[r1] > ranks[r2]) parents[r2] = r1;
	else if (ranks[r2] > ranks[r1]) parents[r1] = r2;
	else parents[r2] = r1, ++ranks[r1];
	return true;
}
int kruskcal(int m)
{
	sort(edge.begin(), edge.end(), cmp);
	int side = 0;
	for (auto& [r1, r2, w] : edge)
	{
		if (side == m - 1) break;
		// union find algorithm
		if (union_find(r1, r2))
		{
			cost -= w;
			++side;
		}
	}
	return cost;
}
int main()
{
	// fast io
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int m, n;
	while (cin >> m >> n, m && n)
	{
		// init
		edge.clear();
		parents.clear();
		ranks.assign(m, 0);
		for (int i = 0; i < m; ++i) parents.emplace_back(i);
		cost = 0;
		// store roads in edges
		while (n--)
		{
			int x, y, z;
			cin >> x >> y >> z;
			cost += z;
			edge.push_back({ x, y, z });
		}
		// kruskcal algorithm
		cout << kruskcal(m) << "\n";
	}
	return 0;
}

# 參考資料

https://www.larrysprognotes.com/UVa%20-%2011631/