Back to Graph Theory Series

Dynamic Programming on Tree Decompositions

October 11, 2026 Wasil Zafar 28 min read

Treewidth reveals when an apparently global graph problem can be solved by remembering only a small boundary at a time.

Contents

  1. Core intuition
  2. Tree decompositions
  3. Nice decompositions
  4. Worked example
  5. Implementation
  6. Complexity
  7. Applications
  8. Pitfalls
  9. Historical context

The Core Intuition: Remember the Boundary

Many hard graph problems feel global because a choice made here can constrain vertices far away. Treewidth asks whether those long-range interactions can be routed through a small interface. If they can, we process most of the graph, forget its internal details, and retain only what the unprocessed part still needs to know.

The doorway analogy

Imagine clearing a building room by room. Once a region is behind you, the future cannot interact with every object in that region; it can interact only through the current doorways. A bag is that doorway. Dynamic programming records every relevant condition at the doorway, not the full history behind it.

This is the same idea behind ordinary dynamic programming on a rooted tree. A child subtree communicates with its parent through one vertex. A tree decomposition generalizes that interface from one vertex to a small set of vertices.

What a Tree Decomposition Must Guarantee

A tree decomposition of $G=(V,E)$ is a tree $T$ whose nodes carry vertex sets called bags, written $B_t\subseteq V$. It is not a spanning tree of the graph: its nodes are bags, bags may overlap, and one graph vertex may occur in several bags.

Vertex coverage

Every graph vertex appears in at least one bag: $\bigcup_{t\in T}B_t=V$.

Edge coverage

For every edge $uv\in E$, some bag contains both $u$ and $v$.

Running intersection

For each vertex $v$, all bags containing $v$ induce a connected subtree of $T$.

The third rule is the subtle one. A vertex may persist across several adjacent bags, but it may not disappear and later reappear. Without that connectedness, a DP could forget a constraint involving the vertex and then encounter the vertex again with no reliable memory of the earlier decision.

A graph and a width-two tree decomposition A chain of three triangles has bags ABC, BCD, and CDE arranged as a path. Every graph edge is contained in a bag and every repeated vertex occurs in consecutive bags. One graph, summarized by three overlapping bags Original graph A B C D E Decomposition tree (here, a path) B₁ = {A, B, C} covers triangle ABC B₂ = {B, C, D} the middle boundary B₃ = {C, D, E} covers triangle CDE largest bag = 3 vertices, so width = 3 − 1 = 2
The repeated vertices form connected runs: B appears in the first two bags, C in all three, and D in the last two. Each edge is visible inside at least one bag.

Width and treewidth

The width of one decomposition is its largest bag size minus one. Treewidth is the smallest width achievable over every valid decomposition:

$$\operatorname{width}(T,\{B_t\})=\max_{t\in T}|B_t|-1,\qquad \operatorname{tw}(G)=\min_{(T,\{B_t\})}\operatorname{width}(T,\{B_t\}).$$

The “minus one” convention makes a nontrivial tree have treewidth $1$: each tree edge can be a bag of size two. A single isolated vertex has treewidth $0$.

Graph familyTreewidthIntuition
Forest with at least one edge$1$Edge-sized bags are enough.
Cycle $C_n$ for $n\ge 3$$2$One extra remembered vertex breaks the cycle into a path.
Clique $K_r$$r-1$All clique vertices must meet in some bag.
Large square gridsgrows with side lengthNo constant-size separator can sweep across the grid.

Sparse does not automatically mean small treewidth

A grid has bounded degree and only linearly many edges, yet its treewidth grows. Planarity, low average degree, and a tree-like drawing are clues—not guarantees. The decomposition itself is the certificate.

The separator fact that makes DP possible

Cut any decomposition-tree edge $tt'$. The intersection $B_t\cap B_{t'}$ separates graph vertices that occur only on one side from vertices that occur only on the other. Therefore, once a rooted DP finishes the subtree below $t$, the rest of the graph can interact with that processed region only through the current bag.

That observation tells us what a state should mean:

A state is a compressed description of how a partial solution behaves on the bag, sufficient to combine it with every possible continuation outside the processed subtree.

Nice Decompositions: Four Reusable Transitions

Arbitrary decompositions can be normalized into a rooted nice tree decomposition. Each node then performs one small structural operation. This usually increases the number of bags only polynomially and makes both proofs and implementations much cleaner.

NodeRelationship to child bag(s)What the DP does
LeafUsually an empty or singleton bagInitialize the base table.
Introduce $v$$B_t=B_c\cup\{v\}$Extend each child state with the legal possibilities for $v$.
Forget $v$$B_t=B_c\setminus\{v\}$Optimize over all possibilities for $v$ because the future can no longer see it.
JoinTwo children have the same bag as $t$Combine compatible partial solutions from disjoint processed regions.

We use bottom-up processing and the convention above. Some books reverse the names “introduce” and “forget” by orienting the tree differently. The formulas are not contradictory; the direction and state invariant must simply be stated.

Information flow in a nice-decomposition DP
flowchart TD
L[Leaf: base state] --> I[Introduce: extend]
I --> F[Forget: optimize]
L2[Other processed branch] --> J[Join: merge]
F --> J
J --> R[Root: final answer]

Worked Example: Maximum Independent Set

An independent set contains no adjacent pair. For a node $t$ and subset $S\subseteq B_t$, define

$$\mathrm{dp}_t[S]=\text{maximum number of selected vertices in the processed subgraph, with }I\cap B_t=S.$$

The invariant carries two kinds of information: $S$ must itself be independent, and the table value remembers the best compatible solution already hidden below the bag.

Introduce and forget transitions for maximum independent set A child bag BC introduces D to form BCD, whose valid independent-set states are empty and each singleton because BCD is a triangle. Forgetting B produces the boundary CD. A state records only what the future can still see Child bag {B, C} processed below introduce D Current bag {B, C, D} these three vertices form a triangle valid {B} {C} {D} rejected {B,C} {B,D} {C,D} {B,C,D} Only four of eight bitmasks survive. forget B Parent bag {C, D} B is now internal Forget transition dpₜ[S] = max(dp_c[S], dp_c[S ∪ {B}])
When B is forgotten, the future cannot distinguish whether B was chosen. We keep the better of the two compatible child states. Red states are discarded immediately because they select an edge.

The transitions

Introduce a vertex $v$

If $v\notin S$, copy the matching child value. If $v\in S$, the state is legal only when $v$ has no selected neighbor in $S$:

$$\mathrm{dp}_t[S]= \begin{cases} \mathrm{dp}_c[S], & v\notin S,\\ \mathrm{dp}_c[S\setminus\{v\}]+1, & v\in S\text{ and }S\text{ is independent},\\ -\infty, & \text{otherwise.} \end{cases}$$

Forget a vertex $v$

The parent state no longer mentions $v$, so take the better child solution with $v$ absent or present:

$$\mathrm{dp}_t[S]=\max\bigl(\mathrm{dp}_c[S],\ \mathrm{dp}_c[S\cup\{v\}]\bigr).$$

Join two branches

The child subgraphs overlap exactly on the bag. If both table values count selected bag vertices, subtract them once after adding:

$$\mathrm{dp}_t[S]=\mathrm{dp}_{c_1}[S]+\mathrm{dp}_{c_2}[S]-|S|.$$

At an empty root bag, the sole table entry is the optimum. For the chain-of-triangles graph above, the maximum independent-set size is $2$; examples include $\{A,D\}$ and $\{A,E\}$.

A manual trace

MomentBoundary knowledgeWhat may be forgotten
Process bag $\{A,B,C\}$Which of A, B, C is selectedNothing yet; all three touch the current boundary.
Move to $\{B,C,D\}$Selections of B, C, DA can be summarized into the best table value because it never appears again.
Move to $\{C,D,E\}$Selections of C, D, EB can be forgotten for the same reason.
FinishNo exposed verticesTake the best complete solution.

Implementation: Stable Bag Indices and Valid Masks

For subset-based problems, assign each vertex in a bag a fixed local bit position. Bit $i$ is $1$ exactly when the $i$th bag vertex is selected. A bag of size $b$ then has $2^b$ raw masks. Precompute which masks satisfy local constraints so transitions never reconsider obviously invalid states.

def valid_independent_masks(bag, edges):
    position = {vertex: i for i, vertex in enumerate(bag)}
    conflicts = [0] * len(bag)

    for u, v in edges:
        if u in position and v in position:
            i, j = position[u], position[v]
            conflicts[i] |= 1 << j
            conflicts[j] |= 1 << i

    valid = []
    for mask in range(1 << len(bag)):
        if all(not (mask & (1 << i) and mask & conflicts[i])
               for i in range(len(bag))):
            valid.append(mask)
    return valid

bag = ["B", "C", "D"]
edges = [("B", "C"), ("B", "D"), ("C", "D")]
print(valid_independent_masks(bag, edges))  # [0, 1, 2, 4]
#include <iostream>
#include <vector>
using namespace std;

int main() {
    // Local order is B, C, D. Each pair is an edge.
    vector<int> conflict = {0b110, 0b101, 0b011};
    vector<int> valid;

    for (int mask = 0; mask < (1 << 3); ++mask) {
        bool ok = true;
        for (int i = 0; i < 3; ++i)
            if ((mask & (1 << i)) && (mask & conflict[i]))
                ok = false;
        if (ok) valid.push_back(mask);
    }
    for (int mask : valid) cout << mask << ' '; // 0 1 2 4
}
import java.util.*;

public class BagMasks {
    public static void main(String[] args) {
        // Local order is B, C, D. Each pair is an edge.
        int[] conflict = {0b110, 0b101, 0b011};
        List<Integer> valid = new ArrayList<>();

        for (int mask = 0; mask < (1 << 3); mask++) {
            boolean ok = true;
            for (int i = 0; i < 3; i++)
                if ((mask & (1 << i)) != 0 &&
                    (mask & conflict[i]) != 0) ok = false;
            if (ok) valid.add(mask);
        }
        System.out.println(valid); // [0, 1, 2, 4]
    }
}

Production invariant

Never infer bit positions from a set's iteration order. Store an explicit ordered vertex list per bag and explicit position maps between parent and child bags. Most “mysterious” treewidth-DP bugs are actually remapping bugs.

Complexity: What the Parameter Really Buys

Let $n=|V|$ and let $k$ be the decomposition width. A subset state has at most $2^{k+1}$ masks per bag, so independent-set and vertex-cover DPs can often run in $O(2^k\,\mathrm{poly}(k)\,n)$ time on a suitable nice decomposition. The exact factor depends on transitions and representation.

Problem styleTypical boundary informationState-growth warning
Independent set / vertex coverSelected subset of the bagAbout $2^{k+1}$ raw subsets.
$q$-coloring / finite-domain CSPOne label per bag vertexAbout $q^{k+1}$ assignments.
Dominating setChosen, dominated, or still needs dominationRoughly three statuses per vertex, plus consistency rules.
Hamiltonian or connectivity problemsDegrees plus a partition/pairing of boundary verticesFar more than subsets; naive partitions can be superexponential in $k$.

The phrase “linear time on bounded-treewidth graphs” means that $k$ and the problem description are treated as fixed. It does not mean the hidden dependence on $k$ is small. Courcelle's theorem is a powerful classification result, but its general construction can carry enormous constants; hand-designed DPs are usually preferable in practice.

The decomposition is part of the cost

Finding a minimum-width decomposition is NP-hard. Practical solvers often use elimination-order heuristics such as minimum degree or minimum fill, exact/FPT routines for small target widths, or decompositions supplied by the application. Always validate the three decomposition axioms before trusting a DP result.

Where Treewidth DP Fits

Selection and covering

Maximum independent set, minimum vertex cover, dominating set, and many packing problems use small per-vertex status alphabets.

Coloring and constraints

Graph coloring, SAT/CSP primal graphs, and scheduling models become tractable when interactions cross small bags.

Connectivity problems

Steiner tree, feedback sets, and Hamiltonian variants are possible, but states must remember how partial paths or components meet the boundary.

Inference and structured models

Probabilistic graphical models, circuit constraints, and some biological interaction models use essentially the same bag-elimination idea.

Treewidth DP is most attractive when an exact answer matters, the width stays modest, and the state can express all boundary interactions compactly. A large, low-degree road or grid network may still have high treewidth; in that case approximation, branch-and-bound, integer programming, or problem-specific separators may be a better choice.

Common Failure Modes

FailureWhy it breaks correctness or performanceRepair
Checking coverage but not running intersectionA forgotten vertex can reappear, so earlier decisions are lost.For every vertex, verify that its bag occurrences induce a connected subtree.
Using an under-specified stateTwo partial solutions that look equal locally may behave differently when extended.State the equivalence invariant: future continuations must see them as interchangeable.
Double-counting at joinsBoth children contain the bag and may both count its contribution.Subtract the shared contribution or adopt a convention that counts vertices when forgotten.
Changing bag bit order silentlyThe same integer mask denotes different vertex subsets across nodes.Build explicit parent-child index maps.
Quoting only $O(f(k)n)$The hidden $f(k)$ may dominate, and decomposition construction is omitted.Estimate actual table counts, transition cost, memory, and preprocessing.
Assuming sparsity implies low widthGrids and expanders can be sparse yet have large treewidth.Compute or estimate a decomposition before committing to the approach.

A practical design checklist

  1. Obtain and validate a decomposition. Record its actual maximum bag size.
  2. Root and normalize it. Make the introduce/forget orientation explicit.
  3. Define one sentence of state semantics. Say exactly what processed subgraph and boundary condition each entry represents.
  4. Derive transitions from that invariant. Include invalid-state handling and join accounting.
  5. Estimate table growth before coding. $2^{k+1}$, $q^{k+1}$, and partitions of a bag behave very differently.
  6. Test on tiny graphs by brute force. Compare every optimum and include malformed decompositions as negative tests.

Structural Theory to an Algorithmic Toolkit

Treewidth grew from structural graph theory and became central to the graph-minors program. Its algorithmic importance comes from a clean bridge: small treewidth means the graph can be assembled through small separators, and small separators mean a finite boundary state can summarize each processed region.

Courcelle's theorem pushes this principle remarkably far: many properties expressible in monadic second-order logic are decidable in linear time on graph classes of bounded treewidth. The theorem explains the breadth of the method; the worked independent-set DP shows the engineering pattern that makes it tangible.