Approximation & Heuristics
In short: brute-force TSP doesn't scale, and no algorithm known today solves it exactly in polynomial time. But Meridian still has to ship a route. The fix is a proven approximation: grow a minimum spanning tree (section 17's relay backbone), walk it so that every edge is visited twice — once in, once out — then shortcut past any city you've already seen. The result is never more than twice the length of the best possible tour, and that's not a hope, it's provable, because the MST weight is itself a lower bound on any tour at all. A different heuristic, nearest-neighbour, has no such proof behind it — it's here for contrast, not as a replacement.

Why the MST is a lower bound at all
Remove any one edge from the optimal tour and what's left is a path touching every city — which is itself a spanning tree, just not necessarily the cheapest one. So:
optimal tour cost ≥ (optimal tour minus one edge) ≥ MST weight
That one inequality is the entire foundation. Nothing about approximating TSP works without first having something to measure against, and the MST is cheap to compute exactly (section 17) even though the tour itself isn't.
The relay backbone's MST, weight 10. Walking every edge out and back would cost 20 — but the shortcut tour never needs to.
The MST here keeps central-north (3), central-harbor (4) and harbor-mill (3): weight 10. This is the lower bound every tour on these four depots has to beat. 4 nodes, 6 edges, undirected.
The shortcut, and why it's always safe
A preorder DFS of the MST visits every node exactly once and never needs to retrace a step it didn't already take to get there — it just skips the "come back here" trips the doubled walk would otherwise make:
The shortcut only works because of the triangle inequality: going directly from A to C is never longer than going A → B → C. Every real distance obeys this. Drop that assumption (make up an edge weight that breaks it) and the shortcut can get worse than the doubled walk, which is why this guarantee is stated for "metric TSP," not TSP in general.
def tour_cost(route, edges):
dist = {}
for a, b, cost in edges:
dist[(a, b)] = cost
dist[(b, a)] = cost
return sum(dist[(route[i], route[(i + 1) % len(route)])] for i in range(len(route)))
edges = [["a", "b", 2], ["b", "c", 2], ["a", "c", 3]]
mst_weight = 4 # a-b and b-c, skipping the dearer a-c
print(tour_cost(["a", "b", "c"], edges) / mst_weight)A heuristic with no proof behind it
Nearest-neighbour — always go to the closest unvisited city — is simpler to write and often does fine in practice, but it has no guaranteed ratio. An adversary can place the cities so that nearest-neighbour's early greedy choices strand it far from where it needs to end up:
This currently returns ['a', 'a', 'b', 'c']. Remove the start city from `unvisited` before the loop begins.
Quick practice
mst_weight = 10
results = [(tc, round(tc / mst_weight, 2), tc / mst_weight <= 2.0) for tc in (14, 20, 21)]
print(results)def ratio_ok(tour_cost, mst_weight):
return tour_cost / mst_weight <= 2.0
print(ratio_ok(10, 5), ratio_ok(9, 5))🎯 Your turn
Build approximate_bid_route(cities, edges). Grow a minimum spanning tree
with Prim's algorithm, walk it in preorder from cities[0], and use that
order as the tour. Return
{"route": ..., "tour_cost": ..., "mst_weight": ..., "ratio": ...} with
ratio rounded to 4 places.
approximate_bid_route(["a","b","c"], [["a","b",2],["b","c",2],["a","c",3]])→tour_cost7,mst_weight4,ratio1.75.
Hint: reuse Prim's from section 17 to grow the tree, then a recursive preorder walk (visit a node, then each unvisited neighbour's whole subtree) turns it into a tour with no edge ever doubled back on.
Press ▶ Run, try the Live App chips, then ✓ Check.
