Given a graph and a set of terminals, find the minimum-weight connected subgraph containing all terminals. It may use extra (“Steiner”) vertices.

Exact: Dreyfus-Wagner —

DP over subsets of terminals: dp[mask][v] = the minimum cost of a tree connecting the terminal set mask and containing vertex .

Two transitions:

  1. Merge two sub-trees meeting at : .
  2. Grow by moving the meeting point along an edge — a Dijkstra over the whole graph for each mask.

See Dreyfus-Wagner for the code. Practical for - with in the thousands.

Approximation

The metric-closure MST — a 2-approximation:

  1. Compute all-pairs shortest paths among the terminals.
  2. Build the complete graph on the terminals with those distances.
  3. Take its MST.
  4. Expand each MST edge back into its shortest path.
// 2-approximation
for each terminal t: dijkstra(t);                       // k runs
build complete graph on terminals with dist[i][j]
mst = kruskal(that graph)
expand each MST edge into its path

Why 2: the optimal Steiner tree, doubled, gives an Eulerian tour of the terminals; shortcutting yields a spanning tree on the terminals of cost , and the MST is no worse. ∎

The best known ratio is (Byrka et al.), via LP rounding.

The variants

VariantComplexity
shortest path, P
MST, P
General NP-hard
smallFPT:
Steiner tree in a treeP — the answer is the virtual tree of the terminals
Steiner forest (pairs to connect)NP-hard; 2-approximation by primal-dual
Directed Steiner tree (arborescence)much harder; no constant-factor approximation known
Euclidean Steiner treeNP-hard; PTAS exists; Steiner points meet at
Rectilinear Steiner treeNP-hard; VLSI routing
Prize-collecting Steiner treeNP-hard; 2-approximation
Node-weighted Steiner tree-approximable

Steiner tree in a tree

When the graph is itself a tree, the minimum connected subgraph containing the terminals is simply the union of the paths between them — computable in with a virtual tree:

Equivalently, sort the terminals by DFS entry time and sum around the cycle, halved.

sort(terms.begin(), terms.end(), byTin);
long long total = 0;
for (int i = 0; i < k; i++)
    total += dist(terms[i], terms[(i + 1) % k]);
total /= 2;

Every edge of the Steiner tree is traversed exactly twice by that cyclic walk — a neat and frequently useful identity.

Where it appears

  • Network design — connect a set of sites at minimum cable cost.
  • VLSI routing — the rectilinear version.
  • Phylogenetics — minimum evolution trees.
  • Contest problems — usually with terminals, signalling Dreyfus-Wagner.

The recognition cue

When a problem says “connect these special vertices at minimum cost” and is suspiciously small () while is large, it is a Steiner tree and the intended solution is the DP. That constraint pattern is the giveaway.

See also: Dreyfus-Wagner · Minimum Spanning Tree · Virtual Tree