Dynamic Programming
Turn repeated computation into a systematic sequence of reusable decisions. Learn to define states, derive recurrences, choose an evaluation order, and recover optimal solutions through Fibonacci, knapsack, matrix-chain multiplication, all-pairs shortest paths, rod cutting, and longest common subsequence. Use the diagrams and worked examples to connect each recurrence with the choices it represents.
By the end of this chapter, you will be able to model a DP state, justify its recurrence, analyze time and space, and reconstruct the decisions behind an optimal answer.
13.1Why dynamic programming?
The recursion repeatedly asks for identical states, such as F(3) or KP(i,W).
An optimal solution can be constructed from optimal solutions of smaller states.
A DP solution is a dependency graph of states. The recurrence specifies the incoming dependencies; memoization or tabulation determines how the graph is evaluated. Correctness comes from showing that the recurrence covers every valid possibility and combines already-correct smaller answers.
13.2Fibonacci: recursion versus DP
def fibonacci(n): if n == 0: return 0 if n == 1: return 1 return fibonacci(n-1) + fibonacci(n-2)
Throughout this chapter, use zero-based indexing: F(0)=0, F(1)=1, and F(n)=F(n−1)+F(n−2). Naive recursion evaluates the same states repeatedly; its running time is Θ(φn), where φ=(1+√5)/2. Memoization evaluates each distinct state once.
Slide 4. All 25 recursive calls. Magenta identifies repeated fibo(4), blue repeated fibo(3), and red repeated fibo(2) states.
On a narrow screen, scroll across the tree to follow every branch.
Slide 5. Repeated fibo(3) and fibo(2) calls read indices 3 and 2 from the array [0,1,1,2,3,5].
On a narrow screen, scroll across the tree to follow every branch.
Memoized Fibonacci · F(5)
Follow the actual left-to-right recursion: call, cache, and reuse.
13.3Top-down and bottom-up DP
Start from the requested input. Recurse only when a needed answer is absent; otherwise return the cached value.
def fib(n, memo): if n < 2: return n if memo[n] == -1: memo[n] = fib(n-1,memo)+fib(n-2,memo) return memo[n]
Start with the smallest states, then build solutions for increasing input values.
def fib(n): if n < 2: return n dp = [0] * (n+1) dp[1] = 1 for i in range(2,n+1): dp[i] = dp[i-1] + dp[i-2] return dp[n]
prev2 and prev1. Each new Fibonacci number replaces the older pair, reducing auxiliary space from O(n) to O(1). For an acyclic dependency graph, both approaches implement the same recurrence. Choose an order in which every dependency is available before its dependent state.13.40/1 knapsack
Each item is either included once or excluded. KP(i,Wc) is the maximum profit using items 1…i with total weight at most Wc.
There are exactly two choices for item i: exclude it and retain capacity Wc, or include it and leave capacity Wc−wi. In both cases, only items 1…i−1 remain available. Taking the maximum covers all feasible subsets. The base case assumes positive item weights, so a zero-capacity knapsack earns zero.
Capacity 15 · Two indivisible items
| Property | Item 1 | Item 2 |
|---|---|---|
| Profit | 5 | 15 |
| Weight | 1 | 15 |
| Profit / weight | 5 | 1 |
Greedy chooses item 1 first
Filled segment: item 1, weight 1.
One decision for every item
Read the recurrence as a decision
KP(i, Wc) asks: “What is the largest profit I can earn using items 1…i, with total weight at most Wc?”
- i
- The current item. Only items 1…i are available.
- Wc
- The capacity available in this subproblem.
- wi
- The weight of item i.
- pi
- The profit earned by taking item i.
No items or no capacity
i = 0 or Wc = 0
Return 0.
Otherwise, check the weight.
Item i is too heavy
wi > Wc
Skip item i. Return KP(i−1, Wc).
The capacity stays the same; only items 1…i−1 remain.
Otherwise, the item fits: wi ≤ Wc.
Compare the two choices
Exclude item iKP(i−1, Wc)
Keep capacity Wc.
Include item ipi + KP(i−1, Wc−wi)
Earn pi and use wi of capacity.
Return max(exclude, include)Both choices continue with items 1…i−1, so item i can be taken only once.
1 · Nothing can be packed
If i = 0 or Wc = 0, there are no items or no capacity left.
2 · Item i is too heavy
If wi > Wc, skip this item. The capacity stays the same.
3 · Item i fits
If wi ≤ Wc, compare the two total profits. Include: gain pi and leave capacity Wc−wi. Exclude: gain nothing from this item and keep capacity Wc.
pi + KP(i−1, Wc−wi),
KP(i−1, Wc))
Apply the recurrence to the PPT example: KP(4, 8)
Item 4 has weight 5 and profit 6, so it fits in capacity 8.
- Exclude item 4: KP(3, 8) = 7.
- Include item 4: 6 + KP(3, 8−5) = 6 + KP(3, 3) = 6 + 2 = 8.
Therefore KP(4, 8) = max(7, 8) = 8. Including item 4 gives the better profit.
13.5Building the knapsack table
Worked example: four items have profits [1,2,5,6] and weights [2,3,4,5], with capacity W=8. State KP(i,w) uses only the first i items and capacity at most w. Each transition reads the previous row, preventing an item from being selected twice.
Bottom-up 0/1 knapsack
Watch each state compare “exclude” with “include”.
profit = [0, 1, 2, 5, 6] weight = [0, 2, 3, 4, 5] kp = [[0] * (W + 1) for _ in range(n + 1)] for i in range(1, n + 1): for w in range(1, W + 1): if weight[i] <= w: kp[i][w] = max(profit[i] + kp[i-1][w-weight[i]], kp[i-1][w]) else: kp[i][w] = kp[i-1][w] return kp[n][W]
Top-down memoized version
def knapsack(i, capacity): if i == 0 or capacity == 0: return 0 if memo[i][capacity] != -1: return memo[i][capacity] result = knapsack(i-1, capacity) if weight[i] <= capacity: result = max(result, profit[i] + knapsack(i-1, capacity-weight[i])) memo[i][capacity] = result return result
13.6Matrix multiplication order matters
Parentheses decide which two matrices to multiply first. That first product determines the dimensions used in the next multiplication, so a different grouping can change the total work.
1 · Read the dimensions and count the work
An m×n matrix has m rows and n columns. For A×B, the number of columns in A must equal the number of rows in B.
- m
- Rows in the left matrix A.
- n
- Columns in A and rows in B: the shared dimension.
- p
- Columns in the right matrix B.
Why the scalar cost is m × n × p
The result has m×p entries. Each entry is a row–column dot product containing n scalar multiplications. Thus the standard method uses (m×p)×n = m×n×p scalar multiplications. We count multiplications here, rather than additions.
2 · Compare the two orders in the PPT
Use the same three matrices throughout. Their sequence stays A1A2A3.
- A1
- 2 × 32 rows, 3 columns
- A2
- 3 × 43 rows, 4 columns
- A3
- 4 × 54 rows, 5 columns
In A1(A2A3), multiply A2A3 first. In (A1A2)A3, multiply A1A2 first. Follow each intermediate result into the second step.
A₁: 2×3 · A₂: 3×4 · A₃: 4×5
A₁(A₂A₃)
Multiply A₂A₃ first
(3 × 4) × (4 × 5)
Result: 3 × 5
Cost: 3 × 4 × 5 = 60
(2 × 3) × (3 × 5)
Result: 2 × 5
Cost: 2 × 3 × 5 = 30
(A₁A₂)A₃
Multiply A₁A₂ first
(2 × 3) × (3 × 4)
Result: 2 × 4
Cost: 2 × 3 × 4 = 24
(2 × 4) × (4 × 5)
Result: 2 × 5
Cost: 2 × 4 × 5 = 40
24 + 40 = 64 scalar multiplications, compared with 60 + 30 = 90 for A1(A2A3). The better grouping saves 26 scalar multiplications.
The intermediate shape is the reason: the two paths first create a 2×4 matrix and a 3×5 matrix respectively. These different shapes change the later cost.
Check the dimensions and arithmetic for all four steps
| Grouping | Multiplication | Result | Scalar cost |
|---|---|---|---|
| A1(A2A3) | A2A3: (3×4)(4×5) | 3×5 | 3×4×5 = 60 |
| A1(A2A3): (2×3)(3×5) | 2×5 | 2×3×5 = 30 | |
| (A1A2)A3 | A1A2: (2×3)(3×4) | 2×4 | 2×3×4 = 24 |
| (A1A2)A3: (2×4)(4×5) | 2×5 | 2×4×5 = 40 |
3 · Extend the same idea to four matrices
The next PPT example appends A4: 5×2. Four matrices have five possible parenthesizations:
- A1(A2(A3A4))
- A1((A2A3)A4)
- (A1A2)(A3A4)
- (A1(A2A3))A4
- ((A1A2)A3)A4
We must compare the total cost of each complete grouping. The matrix-chain DP in the next section reuses the best costs of smaller subchains to find the least-cost grouping.
13.7Matrix-chain dynamic programming
Every parenthesization has a final split k: multiply the result of Ai…Ak by the result of Ak+1…Aj. Their dimensions are p[i−1]×p[k] and p[k]×p[j]. The cost is the best cost of each subchain plus the cost of this final multiplication. Try every legal k and retain the cheapest.
p = [2, 3, 4, 5, 2] · Split at k = 2
- 2p[0]
- 3p[1]
- 4p[2]
- 5p[3]
- 2p[4]
2×3 · 3×4
Result: 2 × 4
m[1,2] = 24
4×5 · 5×2
Result: 4 × 2
m[3,4] = 40
Final multiplication cost: 2 × 4 × 2 = 16.
Fill by increasing chain length
Dimensions p=[2,3,4,5,2].
def matrix_chain_order(p): n = len(p) - 1 m = [[0]*(n+1) for _ in range(n+1)] s = [[0]*(n+1) for _ in range(n+1)] for length in range(2, n+1): for i in range(1, n-length+2): j = i + length - 1; m[i][j] = float('inf') for k in range(i, j): q = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j] if q < m[i][j]: m[i][j], s[i][j] = q, k return m, s
Fill by increasing chain length l. For l=3, the start index i takes values 1 and 2: first solve A₁A₂A₃, then A₂A₃A₄. In each case j=i+l−1. The start index chooses the subchain; k chooses a split within that subchain.
The split table s[i,j] stores the k that achieved the minimum. Reconstruct the order recursively by splitting at s[i,j] until each subchain contains one matrix. There are O(n²) intervals and at most O(n) splits per interval, giving O(n³) time.
13.8All-pairs shortest paths: Floyd–Warshall
Given a weighted directed graph G=(V,E), find the shortest distance between every ordered pair of vertices. The input is an adjacency matrix A and the output is a distance matrix D. Unlike a single-source shortest-path problem, every vertex acts as a source.
The four-vertex example
The lecture graph has seven directed edges: 1 → 2 (4), 1 → 4 (8), 2 → 1 (9), 2 → 3 (2), 3 → 1 (6), 3 → 4 (1), and 4 → 1 (2). Direction matters: opposite edges may have different weights.
Initialize D⁽⁰⁾=A. Every diagonal entry is 0. Use the edge weight for a direct edge and ∞ when no direct edge exists. Rows identify the source i and columns identify the destination j.
| From / To | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 1 | 0 | 4 | ∞ | 8 |
| 2 | 9 | 0 | 2 | ∞ |
| 3 | 6 | ∞ | 0 | 1 |
| 4 | 2 | ∞ | ∞ | 0 |
State and recurrence
Keep the best path that avoids k internally, or combine a path from i to k with a path from k to j. Without negative cycles, a shortest path can be chosen without repeating vertices. Splitting a path that uses k gives two subpaths whose intermediate vertices come from 1,…,k−1. This gives the DP recurrence.
Allow one intermediate vertex at a time
Follow the same improvements as the slides, from D⁽⁰⁾ to D⁽⁴⁾.
Updates at each phase
Each phase checks all 16 ordered pairs. The following entries improve; every other entry keeps its previous value.
| Intermediate vertex k | Improved entries |
|---|---|
| 1 | D[2,4]: ∞ → 17; D[3,2]: ∞ → 10; D[4,2]: ∞ → 6 |
| 2 | D[1,3]: ∞ → 6; D[4,3]: ∞ → 8 |
| 3 | D[1,4]: 8 → 7; D[2,1]: 9 → 8; D[2,4]: 17 → 3 |
| 4 | D[2,1]: 8 → 5; D[3,1]: 6 → 3; D[3,2]: 10 → 7 |
Inspect the matrix after each intermediate vertex
| From / To | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 1 | 0 | 4 | ∞ | 8 |
| 2 | 9 | 0 | 2 | 17 |
| 3 | 6 | 10 | 0 | 1 |
| 4 | 2 | 6 | ∞ | 0 |
| From / To | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 1 | 0 | 4 | 6 | 8 |
| 2 | 9 | 0 | 2 | 17 |
| 3 | 6 | 10 | 0 | 1 |
| 4 | 2 | 6 | 8 | 0 |
| From / To | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 1 | 0 | 4 | 6 | 7 |
| 2 | 8 | 0 | 2 | 3 |
| 3 | 6 | 10 | 0 | 1 |
| 4 | 2 | 6 | 8 | 0 |
| From / To | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 1 | 0 | 4 | 6 | 7 |
| 2 | 5 | 0 | 2 | 3 |
| 3 | 3 | 7 | 0 | 1 |
| 4 | 2 | 6 | 8 | 0 |
Final distances and a shortest route
| From / To | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 1 | 0 | 4 | 6 | 7 |
| 2 | 5 | 0 | 2 | 3 |
| 3 | 3 | 7 | 0 | 1 |
| 4 | 2 | 6 | 8 | 0 |
Pseudocode and Python
Initialize D = a copy of the adjacency matrix
for k = 1 to n:
for i = 1 to n:
for j = 1 to n:
D[i,j] = min(D[i,j], D[i,k] + D[k,j])
return D
def floyd_warshall(A): n = len(A) D = [row[:] for row in A] for k in range(n): for i in range(n): for j in range(n): D[i][j] = min(D[i][j], D[i][k] + D[k][j]) return D inf = float('inf') A = [[0, 4, inf, 8], [9, 0, 2, inf], [6, inf, 0, 1], [2, inf, inf, 0]] D = floyd_warshall(A) print(D[1][0]) # 5: vertex 2 to vertex 1 (zero-based Python indices)
13.9Rod-cutting problem
Prices for lengths 1…10 are [1,5,8,9,10,17,17,20,24,30]. For a rod of length n, choose the first piece i and combine its price with the best revenue for n−i.
Assume integer lengths, a listed price for every supported piece length, and no cutting cost. The whole rod must be sold. Choosing i=n sells the rod intact; choosing a smaller i leaves a remainder whose optimal revenue is r[n−i]. Piece lengths may be reused, unlike items in 0/1 knapsack. Set r[0]=0.
Rod length 4 · Prices: p₁=1, p₂=5, p₃=8, p₄=9
Prices appear above the bar; lengths appear inside each piece.
Bottom-up revenue construction
Every possible first cut is tested for each increasing rod length.
| n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|
| r[n] | 1 | 5 | 8 | 10 | 13 | 17 | 18 | 22 | 25 | 30 |
| best decomposition | 1 | 2 | 3 | 2+2 | 2+3 | 6 | 1+6 or 2+2+3 | 2+6 | 3+6 | 10 |
Slide 62. All 16 calls, including repeated subproblems of lengths 3,2,1,0. Children of length n represent the remaining lengths n−1 down to 0.
On a narrow screen, scroll across the tree to follow every branch.
def cut_rod(p,n): if n == 0: return 0 q = float('-inf') for i in range(1,n+1): q=max(q,p[i]+cut_rod(p,n-i)) return q
T(n)=1+Σj=0n−1T(j), exponential time.
def bottom_up_cut_rod(p,n): r=[0]*(n+1) for j in range(1,n+1): q=float('-inf') for i in range(1,j+1): q=max(q,p[i]+r[j-i]) r[j]=q return r[n]
O(n²) time and O(n) space.
Memoized top-down rod cutting
def memoized_cut_rod(p,n): r=[float('-inf')]*(n+1) def aux(length): if r[length] >= 0: return r[length] if length == 0: q=0 else: q=max(p[i]+aux(length-i) for i in range(1,length+1)) r[length]=q; return q return aux(n)
13.10Longest common subsequence (LCS)
Given two sequences X and Y, find the longest sequence that appears in both in the same relative order. Characters may be skipped, but they may not be rearranged. LCS measures agreement between sequences and is a useful foundation for comparing text and explaining insertion/deletion edits.
ACE is a subsequence of ABCDE: retain A, C, and E and skip B and D. A subsequence preserves order; CA is not a subsequence of ABCDE.
BCD is a substring of ABCDE. ACE is not. The LCS recurrence below solves the subsequence problem; the longest common substring problem needs a different recurrence.
Worked example: the same four letters in both sequences
- X · 7 characters
- ABCBDAB
- Y · 6 characters
- BDCABA
An LCS is BCBA, with length 4. The figure shows where its characters appear in each sequence. The DP table below establishes that no longer common subsequence exists.
Keep order. Gaps are allowed.
One longest common subsequence: BCBA
X positions: 2, 3, 4, 6
Y positions: 1, 3, 5, 6
Define the state using prefixes
With no characters on one side, the common subsequence is empty.
Keep this character and extend the diagonal result by one.
Skip one last character. Keep the better result from above or left.
When the last characters match: an optimal common subsequence can end with that character. Append it to an LCS of the two shorter prefixes. This explains the diagonal dependency and the added 1.
When the last characters differ: they cannot both be the final matched character. Every common subsequence omits at least one of those two positions, so compare the solution that drops X’s last character with the solution that drops Y’s last character. Choose the longer result.
Fill the table row by row, from left to right. Each state uses only the cell above, the cell to the left, or the diagonal cell, all of which are already available. The final length is L[m,n], where m=len(X) and n=len(Y).
LCS length table and traceback
X = ABCBDAB · Y = BDCABA
| i / j | 0 · ∅ | 1 · B | 2 · D | 3 · C | 4 · A | 5 · B | 6 · A |
|---|---|---|---|---|---|---|---|
| 0 · ∅ | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 · A | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| 2 · B | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
| 3 · C | 0 | 1 | 1 | 2 | 2 | 2 | 2 |
| 4 · B | 0 | 1 | 1 | 2 | 2 | 3 | 3 |
| 5 · D | 0 | 1 | 2 | 2 | 2 | 3 | 3 |
| 6 · A | 0 | 1 | 2 | 2 | 3 | 3 | 4 |
| 7 · B | 0 | 1 | 2 | 2 | 3 | 4 | 4 |
Read the highlighted path backwards
Characters collected on the walk:
A, B, C, B
Stop at an empty prefix. On a tie, this example moves up.
Start at the bottom-right value: L[7,6] = 4.
The third characters are both C. Use the diagonal: 1 + L[2,2] = 1 + 1 = 2.
B differs from A. Compare above and left: max(L[6,6], L[7,5]) = max(4,4) = 4. The traceback chooses up on this tie.
Match, compare, and reconstruct
X = ABCBDAB · Y = BDCABA. Build the table, then follow an optimal path backwards.
Follow all eight traceback moves
Character positions in this table are one-based. The collected letters are in reverse order.
| Move | Cells | Decision | Collected |
|---|---|---|---|
| 1 | L[7,6] → L[6,6] | Skip X[7]=B; move up (up wins ties). | — |
| 2 | L[6,6] → L[5,5] | Match A: record it and move diagonally. | A |
| 3 | L[5,5] → L[4,5] | Skip X[5]=D; move up (up wins ties). | A |
| 4 | L[4,5] → L[3,4] | Match B: record it and move diagonally. | AB |
| 5 | L[3,4] → L[3,3] | Skip Y[4]=A; move left. | AB |
| 6 | L[3,3] → L[2,2] | Match C: record it and move diagonally. | ABC |
| 7 | L[2,2] → L[2,1] | Skip Y[2]=D; move left. | ABC |
| 8 | L[2,1] → L[1,0] | Match B: record it and move diagonally. | ABCB |
Stop at L[1,0]. Reverse ABCB to obtain BCBA.
Recover the sequence, not just its length
- Start at L[m,n]. If the current characters match, record that character and move diagonally up-left.
- If they differ, move to whichever of the upper or left cells has the larger value. Either direction is valid when the values tie; here we choose up.
- Stop when either prefix becomes empty. Reverse the recorded characters because the walk collected them from right to left.
def longest_common_subsequence(x, y):
m, n = len(x), len(y)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if x[i - 1] == y[j - 1]:
dp[i][j] = 1 + dp[i - 1][j - 1]
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
i, j = m, n
result = []
while i > 0 and j > 0:
if x[i - 1] == y[j - 1]:
result.append(x[i - 1])
i -= 1
j -= 1
elif dp[i - 1][j] >= dp[i][j - 1]:
i -= 1
else:
j -= 1
return dp[m][n], ''.join(reversed(result))
LCS practice problems with explanations
Find an LCS of X=ABC and Y=AC. Write the final row of the DP table, including the empty-prefix column.
Show solution
The final row is [0,1,2], and the LCS is AC. Matching A creates length 1; B contributes no match; matching C extends the diagonal prefix result to 2. The B in X can be skipped because a subsequence need not be contiguous.
Find an LCS of X=AGGTAB and Y=GXTXAYB. Explain why the answer is valid.
Show solution
The length is 4 and an LCS is GTAB. It appears at positions 2,4,5,6 in X and 1,3,5,7 in Y. The DP recurrence gives L[6,7]=4, establishing that no common subsequence is longer.
Find all distinct LCS strings of X=ABC and Y=BAC. Does one traceback necessarily find all of them?
Show solution
The length is 2. The distinct answers are AC and BC. One traceback chooses one branch when the table values tie, so it returns only one answer. To enumerate all answers, follow every optimal branch and remove duplicates; the number of answers can be large.
What are the LCS length and sequence for (ABCD,EFGH), (empty string,ABC), and (AAAA,AA)?
Show solution
The first two pairs return length 0 and the empty sequence: there are no shared characters in the first pair and no characters available in the empty input. The third returns length 2 and AA. Matching positions must be used in increasing order, so the same character position cannot be reused.
Using only single-character insertions and deletions, how many edits are needed to transform HEAP into PEA?
Show solution
An LCS is EA, with length 2. Keep these characters, delete H and the final P, then insert P at the beginning: 3 edits. In general, the minimum is m+n−2L because m−L characters must be deleted and n−L inserted. This formula does not apply unchanged when substitutions are permitted as a single edit.
13.11Exercises and checks
Use these questions to check both the computed answer and the reasoning behind it.
- Improve Fibonacci auxiliary space to O(1) using
prev2,prev1, andcurrent. - Can DP improve a single factorial computation? No: the recursion has no overlapping subproblems.
- Can DP implement merge sort? Standard merge-sort subproblems are disjoint, not identical, so memoization adds no benefit.
- For each DP algorithm, identify the state, recurrence, base case, evaluation order, and returned state.
- Derive matrix-chain complexity from the length, start-index, and split-index loops.
- Starting from the four-vertex graph, list the entries improved at each Floyd–Warshall phase. Why must the intermediate-vertex loop be outermost?
- Compare the direct edge from 2 to 1 with the route 2 → 3 → 4 → 1. What happens if a reachable negative cycle lies on a route?
Check your worked-example answers
F(5)=5. Knapsack returns profit 8 by selecting items 2 and 4 (total weight 8). Matrix-chain multiplication returns cost 76 with order A₁(A₂(A₃A₄)). Floyd–Warshall returns D[2,1]=5 via 2 → 3 → 4 → 1. A rod of length 8 returns revenue 22 using lengths 2 and 6. The LCS example returns BCBA with length 4.
The outer k loop preserves the allowed-intermediate-vertices invariant. The phases improve 3, 2, 3, and 3 entries respectively. A pair that can pass through a negative cycle has no finite minimum distance.