Minimum spanning trees
Connect every vertex with the least total edge weight: what a spanning tree is, Kruskal’s and Prim’s algorithms, why greedy works here, and how an MST differs from shortest paths.
Spanning trees
Kruskal: cheapest edges first
string names = "ABCDEFG";var roads = new List<(char A, char B, int W)>{ ('E', 'G', 4), ('C', 'E', 2), ('C', 'F', 3), ('A', 'B', 4), ('A', 'D', 2), ('B', 'E', 3), ('B', 'C', 6), ('D', 'E', 5), ('D', 'G', 7), ('F', 'G', 1), ('E', 'F', 6)}; var parent = Enumerable.Range(0, names.Length).ToArray();int Find(int x) => parent[x] == x ? x : parent[x] = Find(parent[x]); int total = 0;var chosen = new List<string>();foreach (var (a, b, w) in roads.OrderBy(r => r.W)) // stable: equal weights keep list order{ int ra = Find(names.IndexOf(a)), rb = Find(names.IndexOf(b)); if (ra == rb) // already connected: would make a loop { Console.WriteLine($"Skip {a}{b}({w}): {a} and {b} are already connected"); continue; } parent[ra] = rb; // merge the two groups chosen.Add($"{a}{b}({w})"); total += w; if (chosen.Count == names.Length - 1) break; // V − 1 edges: done}Console.WriteLine(string.Join(" ", chosen));Console.WriteLine($"Total: {total}");Skip EG(4): E and G are already connected FG(1) CE(2) AD(2) CF(3) BE(3) AB(4) Total: 15
Prim: grow one tree
var graph = new Dictionary<char, List<(char To, int W)>>();void Road(char a, char b, int w){ if (!graph.ContainsKey(a)) graph[a] = new(); if (!graph.ContainsKey(b)) graph[b] = new(); graph[a].Add((b, w)); graph[b].Add((a, w));}Road('A', 'B', 4); Road('A', 'D', 2); Road('B', 'E', 3); Road('B', 'C', 6);Road('D', 'E', 5); Road('C', 'E', 2); Road('C', 'F', 3); Road('E', 'G', 4);Road('D', 'G', 7); Road('F', 'G', 1); Road('E', 'F', 6); var inTree = new HashSet<char>();var pq = new PriorityQueue<(char From, char To), int>();pq.Enqueue(('A', 'A'), 0); // start at Aint total = 0;var added = new List<string>();while (pq.TryDequeue(out var edge, out int w)){ if (!inTree.Add(edge.To)) continue; // already in the tree: skip total += w; if (edge.From != edge.To) added.Add($"{edge.From}{edge.To}({w})"); foreach (var (next, nw) in graph[edge.To]) if (!inTree.Contains(next)) pq.Enqueue((edge.To, next), nw);}Console.WriteLine(string.Join(" ", added));Console.WriteLine($"Total: {total}");AD(2) AB(4) BE(3) EC(2) CF(3) FG(1) Total: 15
Why does greedy work here?
Show a hint
best[i] = cheapest known connection from point i to the tree. Repeat V times: pick the unused point with the smallest best[i], add it, then update best[] for the others using distances from the new point.
Show the solution
int[][] pts = { new[] { 0, 0 }, new[] { 2, 2 }, new[] { 3, 10 }, new[] { 5, 2 }, new[] { 7, 0 } };Console.WriteLine(MinTrenchCost(pts)); static int MinTrenchCost(int[][] p){ int n = p.Length, total = 0; var best = new int[n]; var used = new bool[n]; Array.Fill(best, int.MaxValue); best[0] = 0; for (int step = 0; step < n; step++) { int u = -1; for (int i = 0; i < n; i++) if (!used[i] && (u == -1 || best[i] < best[u])) u = i; used[u] = true; total += best[u]; for (int v = 0; v < n; v++) { int cost = Math.Abs(p[u][0] - p[v][0]) + Math.Abs(p[u][1] - p[v][1]); if (!used[v] && cost < best[v]) best[v] = cost; } } return total;}20
Remember
A spanning tree connects all V vertices with V − 1 edges and no cycles; the MST has the least total weight. Kruskal: sort edges, take each one that joins two different groups (union-find): O(E log E). Prim: grow one tree, always adding the cheapest edge leaving it: O(E log V). Greedy works because of the cut property. An MST does not give shortest paths.