DP on sequences: LIS and coin change
Two dynamic programs on a line: the longest increasing subsequence (the quadratic table and the n log n tails array with binary search) and coin change (the fewest coins, the number of ways, and why the order of the loops decides whether orders are counted). The recurrences, tables filled step by step, running times, why greedy fails for coin change, and the standard traps.
Why it exists
Many optimisation problems are about one sequence: pick the longest increasing run from a list, pay an amount with as few coins as possible. They are the simplest dynamic programs, with a one-dimensional table, and each teaches something specific: LIS shows how a DP can be sped up from to with a cleverly chosen quantity, and coin change shows how the order of the loops changes what is being counted and why a greedy rule that works for ordinary money fails for other coin systems.
Prerequisites
You need the dynamic programming recipe (dynamic programming) and binary search (binary search).
Precise definitions
Increasing subsequence. Positions with (strictly; non-decreasing uses ). The longest increasing subsequence (LIS) has the largest . It need not be unique and it need not be contiguous.
Coin change, fewest coins. Coin values (unlimited supply of each) and an amount ; find the fewest coins whose values sum to (or report that it is impossible).
Coin change, number of ways. The number of different combinations of coins (the order of the coins does not matter: and are one way) with sum . If order matters the count is of sequences (compositions).
How it works
LIS: the quadratic dynamic program
Let be the length of the longest increasing subsequence that ends at position . It is one more than the best over the earlier positions whose value is smaller: Note the definition: is not "the LIS of the first elements" (that is the prefix maximum of ). Time (each looks at all ), space . Storing the position that gave the maximum allows the subsequence to be printed by following these links back from the best .
Longest increasing subsequence: the quadratic dynamic program: start
Computed by a script, not typed- 1L[i] = 1 if no smaller element precedes a[i]
- 2L[i] = 1 + max L[j] over j < i with a[j] < a[i]
| i = 0 | i = 1 | i = 2 | i = 3 | i = 4 | i = 5 | i = 6 | i = 7 | i = 8 | i = 9 | |
|---|---|---|---|---|---|---|---|---|---|---|
| a[i] | 8 | 3 | 7 | 4 | 10 | 5 | 12 | 6 | 11 | 9 |
| L[i] | ||||||||||
| previous position |
Step 0
The array a. L[i] is the length of the longest increasing subsequence that ends at position i; it is one more than the best L[j] over earlier positions j with a[j] < a[i].
The program below computes both the LIS and the coin tables for the array .
#include <stdio.h>
int main(void) {
int a[10] = {8, 3, 7, 4, 10, 5, 12, 6, 11, 9}, n = 10;
int L[10], parent[10]; /* L[i] = length of the longest increasing subsequence that ENDS at a[i] */
int best = 0, last = 0;
for (int i = 0; i < n; i++) {
L[i] = 1;
parent[i] = -1;
for (int j = 0; j < i; j++)
if (a[j] < a[i] && L[j] + 1 > L[i]) { L[i] = L[j] + 1; parent[i] = j; }
if (L[i] > best) { best = L[i]; last = i; }
}
printf("LIS length %d:", best);
int seq[10], k = best;
for (int i = last; i >= 0; i = parent[i]) seq[--k] = a[i];
for (int i = 0; i < best; i++) printf(" %d", seq[i]);
printf("\n");
int tails[10], size = 0; /* tails[l] = smallest possible last element of an increasing subsequence of length l + 1 */
for (int i = 0; i < n; i++) {
int lo = 0, hi = size;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (tails[mid] < a[i]) lo = mid + 1;
else hi = mid;
}
tails[lo] = a[i];
if (lo == size) size++;
}
printf("tails method: length %d\n", size);
int coins[3] = {1, 3, 4}, T = 12;
int fewest[13], ways[13]; /* fewest coins, and the number of different sets of coins, that make each amount */
fewest[0] = 0;
ways[0] = 1;
for (int x = 1; x <= T; x++) { fewest[x] = 1 << 20; ways[x] = 0; }
for (int x = 1; x <= T; x++)
for (int c = 0; c < 3; c++)
if (coins[c] <= x && fewest[x - coins[c]] + 1 < fewest[x]) fewest[x] = fewest[x - coins[c]] + 1;
for (int c = 0; c < 3; c++)
for (int x = coins[c]; x <= T; x++) ways[x] += ways[x - coins[c]];
printf("amount:");
for (int x = 0; x <= T; x++) printf(" %2d", x);
printf("\nfewest:");
for (int x = 0; x <= T; x++) printf(" %2d", fewest[x]);
printf("\nways: ");
for (int x = 0; x <= T; x++) printf(" %2d", ways[x]);
printf("\n");
return 0;
}LIS length 5: 3 4 5 6 11
tails method: length 5
amount: 0 1 2 3 4 5 6 7 8 9 10 11 12
fewest: 0 1 2 1 1 2 2 2 2 3 3 3 3
ways: 1 1 1 2 3 3 4 5 6 7 8 9 11The LIS has length , for example (another is ): not unique.
LIS in : the tails array
Keep an array in which is the smallest possible last element of an increasing subsequence of length among the elements seen so far. This array is always increasing (a smaller tail exists for shorter subsequences). For each new element : binary-search the first tail that is not smaller than and replace it by ; if every tail is smaller than , append (the longest subsequence has grown). The length of the LIS is the final size of the array. Each element costs one binary search: time , space .
Why it is right: replacing a tail by a smaller value keeps the same length but makes the subsequence easier to extend later, and appending happens exactly when extends the longest one. (The array itself is not in general an increasing subsequence of the input, only its size is the answer; to print the sequence you store, for every element, the position of the tail before it.)
Longest increasing subsequence by the tails array (binary search): start
Computed by a script, not typed| step | element | index found | action | tails after | length |
|---|---|---|---|---|---|
| 0 | |||||
| 1 | |||||
| 2 | |||||
| 3 | |||||
| 4 | |||||
| 5 | |||||
| 6 | |||||
| 7 | |||||
| 8 | |||||
| 9 |
Step 0
tails[l] is the smallest possible last element of an increasing subsequence of length l + 1. For each new element, binary search finds the first tail that is not smaller than it and replaces that tail (or appends the element if all tails are smaller). The number of tails is the length of the LIS.
For non-decreasing subsequences the search is for the first tail strictly larger than .
Coin change
Fewest coins. Let be the fewest coins for amount . The last coin has some value , and the rest is an optimal payment of : With the amount and coin values: time , space . This is the dynamic program that does work for every coin system, where greedy (always take the largest coin) fails for some, such as at amount (greedy method): (), while greedy uses .
Number of ways (combinations). Let be the number of multisets of coins with sum . Process one coin value at a time (outer loop over coins, inner loop over amounts): After the coin has been processed, counts the combinations using only the coins processed so far. With the loops the other way round (outer loop over amounts, inner over coins) counts ordered sequences, because any coin may come last.
Coin change with coins 1, 3, 4: start
Computed by a script, not typed| amount | fewest coins | a best last coin | ways (combinations) |
|---|---|---|---|
| 0 | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 |
Step 0
For the coins 1, 3 and 4: the fewest coins for each amount (greedy fails for 6), and the number of different sets of coins that make the amount.
For the coins the table shows and (), and the number of ways to pay is (, , ); the number of ordered ways to pay is (, , , ).
Relations. Coin change with unlimited coins is the unbounded knapsack (each item may be taken any number of times, value , to be minimised). Deciding whether an amount can be paid at all is the subset-sum style problem with time , pseudo-polynomial in like the knapsack (knapsack and others).
Worked examples
EX 1LIS table by hand
Easy. (), (, nothing smaller before it), ( after ), ( after ), ( after or ), ( after ), ( after ), ( after , whose is ), ( after , whose is ), ( after ). The maximum is . The animation reproduces this and the links give .
EX 2The tails array step by step
EasySame array. ; replaces : ; is larger than every tail: append, ; replaces : ; appends: ; replaces : ; appends: ; replaces : ; appends: ; replaces : . Five tails: LIS length . Here the final tails happen to be an increasing subsequence of the input, but in general the array mixes elements of different subsequences: for the input the tails go , , , and is not a subsequence of the input (the comes after the ); only the size is meaningful.
EX 3Coin change by hand
MediumCoins . , , , , , (so ). Number of combinations for : ; ; ; : ways, the table's entry. Greedy would give , three coins.
EX 4Which loop order?
MediumCount the ways to make with coins (combinations : ; sequences : ). Outer loop coins: process : ; process : , : answer (combinations). Outer loop amounts: , , , : answer (sequences). A question must say which is meant.
EX 5Typical questions
GATE level(a) Time of the straightforward LIS DP: ; of the best known: . (b) LIS of : (strictly increasing); LIS of : ; of (strictly): , non-decreasing: . (c) Fewest coins for with : . (d) Time and space of the coin DP for amount and coins: and . (e) Is the coin DP polynomial? It is pseudo-polynomial in . (f) LIS of a sequence of distinct numbers equals the LCS of the sequence and its sorted version.
Cheat box
Cheat box
- LIS DP: over with ; answer (not ); .
- LIS : tails array, binary search for the first tail (strict) and replace, else append; length size; the array is not the sequence.
- Strictly increasing: first tail ; non-decreasing: first tail .
- Fewest coins: , ; works for every coin system; greedy does not.
- Number of combinations: coins outer loop, amounts inner; sequences: amounts outer loop.
- LIS of distinct values LCS with the sorted array.
Traps and GATE patterns
Connections
Builds on
Across subjects
No cross-subject links yet.
Both are instances of dynamic programming with a one-dimensional table; the two-dimensional ones are in LCS, edit distance, knapsack and matrix chain. The coin problem shows where the greedy method fails, and the tails array uses binary search.
PYQs
5 previous year questions from your book. I re-solved each one myself (4 by running or simulating, 1 by reasoning only); the book's key is shown next to my answer. All PYQs
Consider two strings A = {"qpqrr"} and B =
"pqprqrp". Let x be the length of the longest
common
subsequence
(not
necessarily
contiguous) between A and B and let y be the
number of such longest common subsequences
between A and B. Then x+10y =
.- (a)248000
- (b)44000
- (b)19000
- (d)25000 1 otherwise Finally the length of the longest monotonically increasing sequence is Max (L0 , L1 ,....,L n-1 ). Which of the following statements is TRUE?
- (a)The algorithm uses dynamic programming paradigm
- (b)The algorithm has a linear complexity and uses branch and bound paradigm
- (c)The algorithm has a non-linear polynomial complexity and uses branch and bound paradigm.
- (d)The algorithm uses divide and conquer paradigm.
An algorithm to find the length of the longest
monotonically increasing sequence of numbers
in an array A[0: n - 1] is given below. Let L;
denote the length of the longest monotonically
increasing sequence starting at index i in the
array. Initialize L n-1 = 1. For all i such that 0 ≤
i ≤ n – 2
b) F2 F3 only
L = 1 + Li+1
if A[i] < A[i + 1]
i
{
c) F3 F4 only
d) F1,F2 and F4The weight of a sequence a0 , a1 , ..., an–1 of real numbers is defined as a0 + a1 / 2 + ...+ a n–1 /2n–1 . A subsequence of a sequence is obtained by deleting some elements from the sequence, keeping the order of the remaining elements the same. Let X denote the maximum possible weight of a subsequence of a0 , a1 ,..., an-1 . Then X is equal to
- (a)max(Y, a0 + Y)
- (b)max(Y, a0 + Y/2)
- (c)max(Y, a0 + 2Y)
- (d)a0 + Y/2
A sub-sequence of a given sequence is just the
given sequence with some elements (possibly
none or all) left out. We are given two sequences
X[m] and Y[n] of lengths m and n, respectively,
with indexes of X and Y starting from 0.
We wish to find the length of the longest
common sub-sequence (LCS) of x[m] and Y[n]
as l(m,n), where an incomplete recursive
definition for the function l(i,j) to compute the
length of the LCS of x[m] and Y[n] is given
below:
0, if either i = 0 or j = 0
l(i, j) = { expr1, if i, j > 0 and x [i-1] = Y [j -1]
expr2, if i, j > 0 and x [i - 1] ! = Y [j - 1]
Which one of the following options is correct?- (a)expr1 = l(i-1, j) + 1
- (b)expr1 = l(i, j-1)
- (c)expr2 = max (l(i-1, j), l(i,j-1))
- (d)expr2 = max(l(i-1, j-1), l(i,j))
Let A be an array containing integer values. The distance of A is defined as the minimum number of elements in A that must be replaced with another integer so that the resulting array is sorted in non- decreasing order. The distance of the array [2, 5, 3, 1, 4, 2, 6] is .
Self-check
Question 1
Length of the LIS of ?
Show answerHide
, for example (or ).
Question 2
Fewest coins for with and the number of combinations?
Show answerHide
(); combinations: , , , , : (the table gives ).
Question 3
How does the time of the coin DP depend on the amount, and why is it pseudo-polynomial?
Show answerHide
, linear in the value , which is exponential in the number of bits of .
Question 4
In the tails method, what do you do with an element that is smaller than every tail?
Show answerHide
Replace the first tail (it becomes the smallest possible end of a subsequence of length ); the length does not change.
Revision card
One-screen revision
- LIS: (, ), ; tails + binary search ; answer is the max / the size.
- Coins: , ; greedy fails for .
- Ways: coins outer = combinations, amounts outer = sequences.
- Strict: first tail ; non-decreasing: first tail .
References
- ·Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (3rd edition), Problem 15-4 and Exercise 15.4-6 (longest increasing subsequence); Problem 16-1 (coin changing)
- ·Dasgupta, Papadimitriou, Vazirani, Algorithms, Section 6.1 Shortest paths in dags and longest increasing subsequences; Section 6.4 knapsack (unbounded)
- ·Kleinberg and Tardos, Algorithm Design, Section 6.1 and 6.4 (weighted interval scheduling, subset sums)
Verification
The longest increasing subsequence was computed by the quadratic dynamic program and by the tails method and both were compared with the maximum over ALL subsets on 400 random arrays (up to 10 elements, with repeated values). The tails array was replayed with an independent bisect implementation, and the LIS of an array with distinct values was confirmed equal to the LCS of the array and its sorted copy. Coin change for the coins 1, 3, 4 was compared, for every amount up to 12, with exhaustive enumeration of all multisets of coins (fewest coins, and number of combinations); the count of ordered sequences, obtained by the other loop order, was also checked. The step-by-step tables come from the page program with prints added. Textbook chapter references are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (loop counts, recurrences, merge and quicksort counts and averages, hash probes and load-factor formulas, DP tables, MST and shortest-path runs, Floyd matrices); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)