Branch and Bound
A logistics company must assign 20 delivery trucks to 20 routes. The goal is to minimize total fuel cost. Trying every assignment means checking possibilities. That is more than the number of atoms astronomers can usefully count. Yet a dispatcher's laptop solves this in seconds. The trick is not to search faster, but to search less. Often, after exploring only a tiny fraction of a tree, entire branches can be ruled out. They cannot possibly contain the answer. Such branches are discarded without ever being visited. This is the idea behind branch and bound, a general strategy for solving discrete and combinatorial optimization problems exactly. It extends Backtracking and State-Space Search by adding a numerical yardstick — a bound. This bound estimates how good the best solution reachable from a partial choice could possibly be. Whenever that estimate is already worse than a solution already in hand, the branch is pruned. No further exploration is needed, because nothing good can come from it. Branch and bound sits at the heart of production solvers for integer programming, job scheduling, and the traveling salesman problem. It also applies to the 0/1 Knapsack Problem, revisited here from an exact rather than a greedy or dynamic-programming angle. Commercial solvers such as CPLEX and Gurobi are, at their core, enormous branch-and-bound engines. They are highly tuned and wrapped in decades of engineering. Understanding the paradigm demystifies how these black boxes work. It also equips you to build your own exact solvers when off-the-shelf tools do not fit. This article develops branch and bound from first principles. It covers how bounds are constructed and how the search tree is explored. It also explains why the method is guaranteed to find an optimal solution. Typically, it does so while avoiding almost all of the exponential search space. It closes by contrasting best-first and depth-first exploration strategies. Finally, it previews how randomization offers a complementary way to attack the same hard problems. Randomization is the subject of Introduction to Randomized Algorithms.
- Branch and bound solves optimization problems exactly by exploring a tree of partial solutions, cutting off (pruning) branches that provably cannot beat the best solution found so far.
- A bounding function computes, cheaply, an optimistic estimate of what any completion of a partial solution could achieve.
- Pruning rule: if a node's bound is no better than the current best complete solution (the incumbent), discard that node's entire subtree.
- Best-first search explores the most promising node first (often finding good incumbents fast, at higher memory cost); depth-first search uses less memory and gives an anytime solution.
- Worst-case time remains exponential — branch and bound does not change complexity class — but in practice it prunes the vast majority of the search space.
- It generalizes and subsumes plain backtracking, and is the algorithmic core of real integer-programming and combinatorial-optimization solvers.
Branch and bound prunes an exponential search tree by comparing a cheap optimistic bound at each node against the best confirmed solution found so far.
The idea
Branch and bound (B&B) is a design paradigm for combinatorial optimization: problems that ask for the best member of a finite (but astronomically large) set of feasible solutions, according to some objective function (minimize cost, maximize profit, etc.). The search space is organized as a tree, where the root represents "no decisions made yet," each internal node represents a partial solution (some decisions fixed, others still open), and each leaf represents a complete solution. Branching is the act of splitting a node into children by fixing the next undetermined decision variable, exactly as in ordinary backtracking. What distinguishes branch and bound from plain backtracking is the bounding function, , computed at every node. For a minimization problem, is a lower bound on the objective value of any complete solution reachable by extending the partial solution at node . The algorithm also maintains an incumbent: the best complete, feasible solution discovered so far, with objective value . The central pruning rule is:
The logic is airtight: since is a valid lower bound on everything below , and the incumbent already achieves , nothing below can improve on the incumbent. We are not guessing that the branch is unpromising — we have a mathematical certificate that it is. This is what separates branch and bound from heuristic pruning: the bound must be provably optimistic (a lower bound for minimization, an upper bound for maximization), or the method silently loses correctness. Two ingredients therefore determine how well B&B performs on a given problem: how tight the bound is (a bound closer to the true optimum prunes more) and the order of exploration (which node to expand next, since finding a strong incumbent early enables earlier pruning elsewhere). These two axes — bound quality and search strategy — are the entire design space of branch and bound, and the rest of this article develops both.
How it works
Setting up the tree. As in backtracking, we pick an order of decision variables (e.g., "include item in the knapsack or not," "assign job to machine or not"). A node at depth fixes and leaves the rest free. Its children fix to each of its possible values.
The bounding function. At each node, before branching further, we compute : an easily-computed relaxation of the problem restricted to the fixed choices. Common relaxation techniques:
- Linear/LP relaxation: drop integrality constraints (e.g., allow a fractional amount of an item in knapsack) and solve the resulting continuous problem, which is easy (polynomial time) and gives an upper bound (for maximization) on the integer problem.
- Greedy/fractional bound: e.g., in 0/1 knapsack, fill remaining capacity by value density, allowing the last item to be fractional — this reproduces the classical fractional-knapsack bound used to prune the exact integer search.
- Combinatorial relaxation: e.g., in the traveling salesman problem, replace "must form one cycle" with "each city has degree 2" (a 1-tree or assignment-problem relaxation), which is solvable fast and never worse than the true tour cost.
The main loop. We maintain a live list (a set of nodes not yet processed) and the incumbent value (initialized to for maximization, or for minimization, or to the value of some quick heuristic solution to start pruning immediately). We repeatedly select a live node, bound it, and either prune it, replace the incumbent (if it is itself a complete, feasible, improving solution), or branch it into children which join the live list. ``` function BRANCH-AND-BOUND(root, maximize=True): incumbent ← None incumbent_value ← -infinity if maximize else +infinity live ← {root} // priority queue or stack while live is not empty: n ← SELECT-AND-REMOVE(live) // strategy-dependent if n is INFEASIBLE: continue // prune: dead branch bound ← BOUND(n) // cheap optimistic estimate // Pruning test: can n possibly beat the incumbent? if maximize and bound ≤ incumbent_value: continue // prune: cannot improve if not maximize and bound ≥ incumbent_value: continue // prune: cannot improve if n is a COMPLETE solution: if IS-BETTER(n.value, incumbent_value, maximize): incumbent, incumbent_value ← n, n.value continue for child in EXPAND(n): // branch on next variable live.insert(child) return incumbent, incumbent_value ``` `SELECT-AND-REMOVE` encodes the exploration strategy:
- Depth-first branch and bound (DFBB): `live` is a stack (or implemented via recursion). Memory is , matching plain backtracking, and a complete feasible solution — an anytime answer — is available early and steadily improved.
- Best-first branch and bound (BFBB): `live` is a priority queue ordered by bound (most promising bound first). This tends to find near-optimal incumbents very quickly and prune enormous portions of the tree early, but the live list can grow to hold exponentially many nodes, costing more memory.
- Hybrids: many practical solvers use best-bound-first with depth-first dive: dive greedily to find incumbents fast, then switch to best-first once the tree is partially explored — this is essentially what production MIP solvers do.
The `BOUND` function is problem-specific; for 0/1 knapsack, with items sorted by value/weight ratio, a partial assignment's bound is the value already accumulated plus the fractional-knapsack value obtainable from remaining items and remaining capacity. This is precisely the greedy relaxation from the fractional knapsack problem, repurposed as a pruning oracle rather than as a final answer.
Complexity & correctness
Correctness. The proof rests on one invariant maintained throughout the algorithm: > Invariant: at every point during execution, the true optimal solution's value is either already stored as `incumbent_value`, or it lies within the subtree rooted at some node still in `live`. Initially this holds trivially: the optimum, wherever it is, lies in the subtree rooted at `root`, which is in `live`. Each iteration either (a) discovers a complete feasible solution and updates the incumbent if it improves, which preserves the invariant since that leaf's own subtree is now "accounted for"; or (b) prunes a node because is no better than `incumbent_value`. This is safe precisely because is a valid bound: it means every completion within 's subtree has objective at least as bad as (or equal to) the incumbent, so the optimum cannot be strictly inside that subtree unless it ties the incumbent, in which case the incumbent already represents an optimal value; or (c) branches into children whose union of subtrees is exactly 's subtree (since branching is a case split over the next decision variable), so the invariant transfers from to its children. When `live` becomes empty, every subtree has either been fully explored (its leaves compared against the incumbent) or pruned with a certificate that it holds nothing better. By the invariant, the optimum must therefore equal `incumbent_value`. This argument requires two conditions to hold: the bounding function must be a valid relaxation bound (never worse than what any completion can achieve), and branching must be exhaustive and non-overlapping for the invariant's transfer step to be valid.
Time complexity. In the worst case, no pruning occurs and B&B degenerates to exhaustive backtracking search: if there are binary decision variables, the tree has up to nodes, and with or work to compute a bound per node, worst-case time is for some polynomial (the per-node bounding cost). Branch and bound does not change the worst-case complexity class of an NP-hard problem — it remains exponential in the worst case, since a polynomial worst-case bound would imply P = NP for NP-hard optimization problems (assuming standard complexity assumptions). What changes, often dramatically, is the typical number of nodes actually visited, because tight bounds prune large subtrees wholesale rather than enumerating them leaf by leaf. Empirically, for well-bounded formulations (tight LP relaxations, good variable-ordering heuristics), B&B routinely solves instances with hundreds or thousands of variables that a naive enumeration could never touch, though pathological instances still exist where pruning is minimal.
Space complexity. Depth-first B&B uses space where is the tree depth (typically ), since only the current root-to-node path plus siblings need to be retained — matching ordinary backtracking. Best-first B&B can require space proportional to the number of live nodes simultaneously held in the priority queue, which in the worst case is exponential, trading memory for the tendency to find strong incumbents (and hence prune) sooner. This is the classic time–memory tradeoff between the two exploration strategies, and it is why many industrial solvers default to depth-first or hybrid strategies rather than pure best-first.
Honesty about tradeoffs. The performance of B&B is only as good as its bounding function. A loose bound (far from the true optimum) prunes little and the method degrades toward brute force; a bound that is expensive to compute per node can dominate runtime even if it prunes well. Designing a good bound is often the hardest and most problem-specific part of applying this paradigm — there is no free lunch, and this is exactly why real solvers invest heavily in relaxations (LP relaxations, Lagrangian relaxations, semidefinite relaxations) tailored to each problem family.
Worked example
Consider a 0/1 knapsack instance with capacity , and four items sorted by value density (a prerequisite step, as in fractional knapsack):
| item | weight | value | |
|---|---|---|---|
| A | 4 | 12 | 3.00 |
| B | 5 | 10 | 2.00 |
| C | 3 | 5 | 1.67 |
| D | 6 | 9 | 1.50 |
We maximize total value subject to . The bound at a node with items decided (chosen value , weight used , remaining capacity ) is the fractional-knapsack upper bound: take remaining items greedily by density, allowing the last one taken to be fractional.
Root (no decisions): greedily fill capacity 10 by density — take A (4, value 12, remaining cap 6), take B (5, value 10, remaining cap 1), then of C (value ). Bound . `incumbent_value = -∞` (or seed with a quick greedy integer solution, value 22, from A+B+ (skip C, D since they don't fit) — let's seed incumbent at 22 for a faster trace).
Branch on A (include/exclude):
- A = 1 (weight 4, value 12, remaining cap 6): bound = . Take B (5, val 10, cap left 1), then of C: . Live, bound .
- A = 0 (weight 0, value 0, remaining cap 10): bound = frac-fill(10 over B, C, D) = B(5,10)+C(3,5)+ then 2/6 of D = . Since , prune — this whole branch, and everything under "exclude A," is discarded without ever considering B, C, or D individually along it.
Already, roughly half the tree (all subtrees under ) vanishes in one comparison.
Continue under A = 1, branch on B:
- A=1, B=1 (weight 9, value 22, remaining cap 1): bound = . Only room for of C: . Live.
- A=1, B=0 (weight 4, value 12, remaining cap 6): bound = = C(3,5) fully, then of D (val 4.5): . Since , prune.
Continue under A=1, B=1, branch on C (remaining cap 1, C weighs 3 — infeasible to include):
- C=1: infeasible (weight would be ) — prune as infeasible.
- C=0 (weight 9, value 22, remaining cap 1): bound = . D weighs 6, so only of D: . Still , must branch on D.
Branch on D (remaining cap 1, D weighs 6 — infeasible to include):
- D=1: infeasible, prune.
- D=0: complete solution, weight 9, value 22. Compare to incumbent (22): tied, not strictly better, discard as a duplicate optimum (or accept as an alternate optimal solution, per implementation choice).
At this point `live` is empty (every branch has been pruned or exhausted), so the algorithm terminates with `incumbent_value = 22`, achieved by . Out of possible leaf assignments, the algorithm generated on the order of 7 internal nodes and evaluated only 2 complete leaves explicitly — the rest were eliminated by the bound before ever being enumerated. This is the essential empirical payoff of branch and bound: exponential search space, near-linear number of nodes actually touched, on a well-structured instance.
Common pitfalls & traps
| Pitfall | Why it breaks the algorithm |
|---|---|
| Using a bound that is not truly optimistic (e.g., an underestimate for a maximization bound, or double-counting resources) | Correctness fails silently: valid solutions may be pruned away, and the algorithm returns a suboptimal answer while appearing to work |
| Forgetting to check feasibility separately from the bound | A node can have an excellent bound value yet correspond to no feasible completion (e.g., capacity already exceeded); must be pruned as infeasible, not evaluated as a candidate |
| Comparing bound to incumbent with the wrong inequality direction | For maximization, prune when `bound ≤ incumbent_value`; flipping this either prunes valid optima or fails to prune anything, destroying either correctness or performance |
| Seeding the incumbent poorly (e.g., with an expensive bounding routine) | No pruning occurs until the first complete solution is found, so early search wastes time exploring a nearly-unpruned tree; a quick greedy seed dramatically improves early pruning |
| Best-first search exhausting memory | The live-node priority queue can grow exponentially; without a node limit or switch to depth-first, the algorithm can run out of memory before finding an answer, even though CPU time would have sufficed |
| Recomputing the bound from scratch at every node instead of incrementally updating it | Turns a fast paradigm into a slow one; well-implemented B&B updates bounds incrementally as branching decisions are added |
Pioneers & history
Branch and bound emerged from operations research in the 1960s, as researchers sought exact methods for integer and combinatorial optimization problems that defied both closed-form solutions and pure enumeration. The name itself, and the first general formalization applied to the traveling salesman problem, is credited to Ailsa Land and Alison Doig, whose 1960 paper on integer programming introduced the branching-and-bounding structure used essentially unchanged in modern solvers.
Origins · 1 minAilsa Land and how this was discovered
The paradigm's staying power comes from its generality: any problem whose feasible region can be split into cases and whose relaxations are tractable admits a branch-and-bound treatment, which is why it remains the backbone of exact combinatorial optimization six decades after its introduction.
Where this matters
Commercial mixed-integer programming solvers — CPLEX, Gurobi, SCIP, CBC — are, at their core, industrial-strength branch-and-bound engines, augmented with cutting planes (branch-and-cut) and column generation (branch-and-price). These solvers underpin airline crew scheduling, supply-chain and vehicle-routing optimization, portfolio construction under discrete constraints, and chip-placement and VLSI design, where the decision variables are inherently discrete (assign, route, place) and exact optimality guarantees are valuable, not just approximate heuristics. Compilers and constraint solvers use branch and bound (often under the name "constraint propagation with pruning") for register allocation and instruction scheduling subproblems where an optimal assignment within a small search space is worth computing exactly. Game-tree search in classical AI — exemplified by alpha-beta pruning in chess and other perfect-information games — is a close cousin of branch and bound, using a similar "prune once you know this branch cannot change the outcome" logic, though there the bound comes from adversarial minimax reasoning rather than a relaxation. The traveling salesman problem, still one of the most-studied NP-hard problems, has been solved exactly for instances with tens of thousands of cities using branch-and-bound/branch-and-cut hybrids — a testament to how far intelligent pruning can push exact methods beyond what naive complexity bounds would suggest is practical. Where instance sizes grow beyond what even excellent bounds can tame, practitioners often turn to approximation algorithms or, as previewed by Introduction to Randomized Algorithms, randomized and metaheuristic methods that trade the guarantee of optimality for tractable running time.
Ask the tutor: How would you design and prove a valid bounding function for a new combinatorial optimization problem you have not seen before?
Is branch and bound a heuristic or an exact algorithm?
It is exact. Unlike heuristics or metaheuristics, branch and bound is guaranteed to return a provably optimal solution if run to completion, because every pruning decision is backed by a mathematical certificate (a valid bound) rather than a guess. The exponential worst-case behavior it inherits from NP-hardness is unavoidable for any exact method on NP-hard problems, but correctness — as opposed to speed — is never sacrificed. If you need speed guarantees rather than optimality guarantees, you would instead reach for approximation algorithms or heuristics, possibly using B&B's bounding ideas as a subroutine or comparison baseline.
How does branch and bound relate to plain backtracking?
Branch and bound is backtracking plus a bounding function used for pruning. Ordinary backtracking, as in Backtracking and State-Space Search, prunes only based on feasibility — a partial assignment is abandoned if it violates a constraint. Branch and bound adds a second, more powerful pruning criterion based on optimality: a partial assignment is abandoned even if it is entirely feasible, as long as it cannot possibly lead to a better solution than one already found. This second criterion is what allows B&B to prune vastly more of the tree on optimization problems than feasibility-only backtracking ever could.
Why doesn't branch and bound improve the worst-case complexity of NP-hard problems?
Because in the worst case, an adversarially constructed instance can make every bound exactly tight against every branch, forcing the algorithm to explore essentially the entire tree before finding and confirming the optimum. No bounding function can guarantee polynomial-time pruning on all instances of an NP-hard problem, since that would imply a polynomial-time algorithm for an NP-hard problem, contradicting standard complexity assumptions (unless P = NP). What branch and bound changes is the typical-case or practical running time on structured, real-world instances, which is often dramatically better than the theoretical worst case.
When should I choose best-first over depth-first exploration?
Choose best-first when memory is abundant and finding a strong incumbent quickly is valuable, for instance when you want tight optimality certificates early or need to stop early with a good bound on remaining suboptimality. Choose depth-first when memory is constrained, when you need an anytime algorithm that always has some feasible solution ready if interrupted, or when the problem's tree is very deep. Many production solvers use a hybrid: dive depth-first to quickly find a feasible incumbent, then switch to best-first exploration ordered by bound quality once several nodes are available for comparison.
Can branch and bound be parallelized?
Yes, and this is common in industrial solvers. Because the live-node list represents independent subtrees once initial branching has occurred, different processors or threads can expand different nodes concurrently, sharing a global incumbent value (protected by synchronization) so that all threads benefit from pruning improvements found anywhere in the search. The main engineering challenge is load balancing, since subtree sizes are unpredictable and can vary by orders of magnitude, and ensuring the shared incumbent updates do not become a serialization bottleneck.
Does a tighter bounding function always mean a faster algorithm?
Not necessarily — there is a tradeoff between bound tightness and the cost of computing it. A very tight bound (e.g., solving a near-exact relaxation) prunes more nodes but may take much longer to compute per node; a looser but very cheap bound (e.g., a simple sum) prunes less but lets you process many more nodes per second. The best choice is problem-dependent, and practical solvers often use a cheap bound for most nodes and a more expensive, tighter bound only when the cheap bound is close to the incumbent, forming a strategic middle ground.
How does branch and bound relate to dynamic programming approaches to the same problems, like 0/1 knapsack?
Dynamic programming for 0/1 knapsack builds a table over all (item, capacity) pairs, guaranteeing polynomial time in the numeric value of capacity (pseudo-polynomial), but this can be prohibitively large if capacity is huge. Branch and bound instead explores a decision tree directly and relies on pruning rather than tabulation, so its efficiency depends on how well the bound separates good from bad branches rather than on the numeric size of capacity. In practice, the two can be combined: DP-style bounds can be embedded inside a B&B search, and B&B is often preferred when the state space for DP would be too large to tabulate explicitly.