INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES
01
00/11
01 / COVER STEP 16 · DYNAMIC PROGRAMMING
INVARIANT · STEP 16 · DECK 2 OF 4
PICK OR DON'T

Every problem in this deck is the same two-line recurrence: take this element and reduce the target, or skip it and don't. What changes is only what you do with the two branches — OR them for reachability, ADD them to count, MIN or MAX them to optimise. Eleven lectures, one recurrence, and a second index that is no longer a position but a sum you still owe.

11Problems
4Families
11Units
5Live benches
← → ↑ ↓  or  W A S D  navigate SPACE next   DOUBLE-CLICK to advance I index   G goto problem   P predict H hide solutions   T close video   F fullscreen
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
02 / START HERE READ THIS ONE FIRST

HOW THIS DECK WORKS

This is not a list of problems. It is 11 learning units, and every one runs the same four beats. Go in order and the problems stop feeling random.

ASSUMEDDeck I of this step — the four forms of a recurrence, and pick / not-pick from problem #05. Every recurrence here is pick / not-pick with a RUNNING TARGET as the second index instead of a position. Deck I ended on its hardest slide — two movers on one grid, three indices. This one opens easier on purpose and does not build on that: the last unit you actually need is #05, pick / not-pick over a single index. If unit 1 feels like a step backwards, that is the shape of the step, not a lecture you missed.

01 INTROWhat the concept is, and what to watch for
02 VIDEOThe lecture, full-width in theatre mode
03 DRILLS2–4 questions checking the lecture landed
04 PROBLEMSThe sheet problems that concept unlocks

Moving around

← ↑Back a slide — or A / W → ↓Forward — or D / S / Space 2×clickDouble-click the right side to advance, left to go back. A single click never moves the deck. IThe index: every problem, clickable, with your progress GJump straight to a problem by its number FFullscreen

While you study

HHide solutions — blurs code and steps so you try first PPredict mode: call the next step before the animation plays it TClose the video — Esc works too ☐ ★Mark solved, or star to revisit. Both are saved automatically.

11 PROBLEMS · EVERY ONE LINKS TO A JUDGE TO SOLVE

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
03 / SIGNALS WHEN YOU SEE X, REACH FOR Y

ONE RECURRENCE, FIVE OPERATORS

Every problem in this deck picks or skips an element and reduces a target. What differs is only what you do with the two branches — and which of these six phrasings the statement uses tells you which. The recurrence is never the hard part.

“IS THERE A SUBSET THAT…”

you only need to know whether it can be done, so the two branches are OR-ed

REACHABILITY · boolean table, memo sentinel is −1O(n·T)
“HOW MANY SUBSETS / WAYS”

pick and not-pick are disjoint, so their counts add — and a zero doubles them

COUNTING · same table, ADD the branchesO(n·T)
“MINIMUM COINS”, “MAXIMUM VALUE IN A BAG”

optimise over the same two branches, and make an impossible state unattractive

KNAPSACK · MIN or MAX, ±∞ for impossibleO(n·W)
“UNLIMITED SUPPLY”, “USE A COIN ANY NUMBER OF TIMES”

the item is still available after you take it, so the take branch does not move on

UNBOUNDED · take stays at the same indexO(n·W)
“EQUAL HALVES”, “DIFFERENCE OF D”, “ASSIGN ± SIGNS”

not new problems — algebra turns each into a subset sum for one specific target

REDUCTION · solve, then reuseO(n·T)
A GREEDY ANSWER THAT LOOKS OBVIOUS

check it against a counterexample before writing it; on coins {9,6,5,1} it loses

STOP · the obvious choice is not always optimal
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
04 / CONSTRAINTS BEFORE YOU WRITE A LINE

READ THE ANSWER OFF THE CONSTRAINTS

The cost of every DP here is (number of states) × (work per state), and the state is (index, target). So the TARGET's magnitude decides whether a table is possible at all — which is why a huge target means the answer is not DP, however much the statement looks like it.

n ≤
BUDGET
WHAT THAT BUYS YOU
n ≤ 20
O(2ⁿ)
enumerate every subset — fine for tiny arrays, and the signal for bitmask DP
n·T ≤ 10⁶
O(n·T)
the home row for this entire deck: one cell per (index, target still owed)
T ≤ 10⁴, n ≤ 10²
O(n·T)
a table of 10⁶ cells fills comfortably; this is the usual knapsack shape
T up to 10⁹
not DP
the target cannot index an array — the answer is maths or meet-in-the-middle, not a table
space
O(T)
every recurrence here reads one row back, so one array of T+1 replaces the grid

This is the one deck where a bound on the VALUES matters more than a bound on n: 20 items with a target of 10⁹ is not a knapsack, it is meet-in-the-middle.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
05 / WARMUP BEFORE ANY OF IT · 1 OF 2

THE SECOND INDEX IS A DEBT, NOT A PLACE

DRILL 01 · RECALL

In deck I the second index of a 2D table was a position in a grid. In this deck it is something else. What?

A debt, not a place. Taking an element pays part of the target off; skipping leaves it owed. Once the second index is a number rather than a coordinate, subset sum, knapsack, coin change and rod cutting are visibly the same table — which is the claim this whole deck is built to make.

DRILL 02 · RECALL

A reachability DP returns true or false. Why must its memo table hold int rather than bool?

Three states, two answers. The memo needs true, false and unvisited; the first two are the answer, so the sentinel must be a third value. It is the one mechanical difference between a boolean DP and a numeric one, and it is why these tables are int filled with −1.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
06 / WARMUP BEFORE ANY OF IT · 2 OF 2

THE SECOND INDEX IS A DEBT, NOT A PLACE

DRILL 01 · BUG

Counting subsets that sum to a target. It returns the right answer on most inputs and silently halves it on some. Which line?

int f(int i, int t, vector<int>& a, vector<vector<int>>& dp){
    if(i == 0) return (t == a[0] || t == 0) ? 1 : 0;
    if(dp[i][t] != -1) return dp[i][t];
    int notPick = f(i-1, t, a, dp);
    int pick = (a[i] <= t) ? f(i-1, t - a[i], a, dp) : 0;
    return dp[i][t] = notPick + pick;
}

A zero can be taken or skipped. If a[0] == 0 and t == 0, both the empty subset and {0} are valid, so the base case owes 2. It never fires on arrays of positive integers — which is why the earlier lecture's constraints hid it and the later one's exposed it.

DRILL 02 · RECALL

Coins {9, 6, 5, 1} and a target of 11. Greedy takes the largest coin it can, repeatedly. Is it right here?

Greedy takes the 9 and is then stuck paying 2 with two 1s. Taking no large coin at all gives 6 + 5. A coin of value 1 guarantees an answer EXISTS, never that greedy finds the best one. These are the lecture's own denominations, and unit 07 fills the table beside greedy's run.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
07 / ROADMAP WHAT YOU ARE ABOUT TO DO

THE WHOLE RUN, 11 UNITS

Eleven lectures, 4h 46m. Units 01 to 05 build one table and change one operator at a time; unit 06 names the problem all of them are — 0/1 knapsack, which the sheet has no row for — and units 07 to 11 are that problem with unlimited supply. Unit 07 carries two rows on purpose: one where greedy is right and one where it is wrong.

UNIT 01

REACHABILITY

▶ 38:493 DRILLS1 PROBLEM
UNIT 02

EQUAL HALVES

▶ 9:433 DRILLS1 PROBLEM
UNIT 03

MINIMISE THE GAP

▶ 29:503 DRILLS1 PROBLEM
UNIT 04

COUNT, DON'T CHECK

▶ 36:573 DRILLS1 PROBLEM
UNIT 05

GIVEN DIFFERENCE

▶ 18:003 DRILLS1 PROBLEM
BEYOND THE SHEETUNIT 06

0/1 KNAPSACK

▶ 41:193 DRILLSNO SHEET ROW
UNIT 07

WHEN GREEDY LIES

▶ 34:153 DRILLS2 PROBLEMS
UNIT 08

SIGNS ARE A PARTITION

▶ 9:043 DRILLS1 PROBLEM
UNIT 09

COUNT WITH REFILLS

▶ 22:173 DRILLS1 PROBLEM
UNIT 10

UNBOUNDED KNAPSACK

▶ 22:543 DRILLS1 PROBLEM
UNIT 11

ROD CUTTING

▶ 22:543 DRILLS1 PROBLEM
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
08 / INDEX PRESS I FROM ANYWHERE

INDEX — ALL 11 PROBLEMS

Click any problem to jump straight to it. Solved ones turn green. Press I from anywhere to come back here. The number is the sheet's row, not your position — the deck teaches in the order that builds, so you will meet them slightly out of numerical sequence.

DP on Subsequences · 11
SOLVED HAS A JUDGE LINK
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
09 / INTRO UNIT 01 · REACHABILITY

UNIT 01 — REACHABILITY

A subset-sum table, where the second index is the sum you still owe

THE QUESTION THIS LECTURE ANSWERS

CAN ANY SUBSET OF THIS ARRAY ADD UP TO EXACTLY THIS TARGET?

subsequencepick / not-pickreachabilitymemo sentinel
WHAT TO WATCH FOR
  • 01THE DEFINITION HE CALLS “VERY IMPORTANT”: CONTIGUOUS OR NON-CONTIGUOUS
  • 02TAKING AN ELEMENT REDUCES THE TARGET — THE SECOND INDEX IS A DEBT
  • 03THE MEMO IS int FILLED WITH −1, NOT bool: TRUE AND FALSE ARE BOTH ANSWERS
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
10 / VIDEO UNIT 01 · REACHABILITY

DP 14 · Subset Sum Equals to Target

STRIVER A2Z
Subsequences · pick / not-pick with a target · why the memo cannot be bool
RUNTIME 38:49
AFTER THIS → 3 DRILLS · PROBLEM #01
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
11 / DRILL UNIT 01 · REACHABILITY · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

The lecture opens by defining a subsequence, and stresses one word. Which definition is right?

Contiguous or non-contiguous — he says 'very important' on exactly that word. It matters because it is what licenses pick / not-pick: if subsequences had to be contiguous you would be choosing a window, not choosing per element.

DRILL 02 · RECALL

The answer at each state is a boolean. Why can the memo table NOT be a plain array of booleans?

A memo needs three states: true, false, and unvisited. Two of them are the answer, so the sentinel has to be a third value — which is why the table is int filled with -1 even though the function returns a bool. This is the one place a boolean DP differs mechanically from a numeric one.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
12 / DRILL UNIT 01 · REACHABILITY · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

The second index of the table is the target. What does taking an element do to it?

The second index stops being a position and becomes what you still owe. Taking an element pays part of the debt; skipping leaves it. Once that clicks, every remaining problem in this deck is the same table with a different operator over the two branches.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
13 / MECHANISM UNIT 01 · SUBSETSUM · CODE MIRRORED

THE SECOND INDEX IS NO LONGER A POSITION

[2, 3, 5], target 8. Rows are how much of the array you may use; columns are what you still owe. Every cell asks one question. Can this prefix make this sum, and answers it by OR-ing two cells in the row above: skip me, or pay me off the target. Same table you filled for a grid, indexed by a debt.

RECURRENCE
STATE
CODE MIRROR
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
14 / PROBLEM #01 · REACH · HARD

Subset sum equal to target (DP- 14)

HARD reach ▶ SOLVE ON GEEKSFORGEEKS
SIGNAL — WHAT GIVES IT AWAY

“Is there a subset that sums to…” — you are asked only whether it can be done, not how many ways or how cheaply. That makes the answer a boolean and the two branches an OR.

INTUITION

At every element there are exactly two futures: take it, which pays part of the target off, or skip it, which does not. The target is therefore not a position but a DEBT, and the table is indexed by how much of it is still owed. Every other problem in this deck is this table with a different operator.

STEPS
  1. State: f(i, t) = can a subset of a[0..i] sum to exactly t?
  2. Transition: f(i-1, t) OR f(i-1, t - a[i]), the second only when a[i] <= t.
  3. Base: t = 0 is always true (the empty subset); row 0 is true only at a[0].
  4. Memoize with an int filled with -1 — a bool has no room for 'unvisited'.
  5. Tabulate, then keep one row: the recurrence only reads the row above.
BRUTEO(2ⁿ)
OPTIMALO(n·T)
↕ SCROLL
bool subsetSum(vector<int>& a, int T) {
    int n = a.size();
    vector<bool> prev(T + 1, false), cur(T + 1, false);
    prev[0] = true;                        // empty subset pays 0
    if (a[0] <= T) prev[a[0]] = true;
    for (int i = 1; i < n; i++) {
        cur[0] = true;
        for (int t = 1; t <= T; t++) {
            bool notPick = prev[t];
            bool pick = (a[i] <= t) ? prev[t - a[i]] : false;
            cur[t] = notPick || pick;      // OR: reachable at all?
        }
        prev = cur;
    }
    return prev[T];
}
TIMEO(n·T)one cell per index and target
SPACEO(T)one row, rolled
TRAP

Memoizing into a vector<vector<bool>>. True and false are both answers, so there is no value left to mean 'not computed yet' — every lookup either recomputes or returns a stale false. The answer stays right and the runtime stays exponential.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
15 / INTRO UNIT 02 · EQUAL HALVES

UNIT 02 — EQUAL HALVES

An equal partition is a subset sum for half the total

THE QUESTION THIS LECTURE ANSWERS

CAN THIS ARRAY BE SPLIT INTO TWO PARTS WITH THE SAME SUM?

reductionparity checkhalf the total
WHAT TO WATCH FOR
  • 01THE SUM MUST BE EVEN — AN ODD TOTAL IS FALSE WITH NO TABLE AT ALL
  • 02AFTER THAT IT IS SUBSET SUM FOR total / 2 AND NOTHING ELSE
  • 03NINE MINUTES, BECAUSE THERE IS NO NEW RECURRENCE IN IT
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
16 / VIDEO UNIT 02 · EQUAL HALVES

DP 15 · Partition Equal Subset Sum

STRIVER A2Z
The parity check that comes before any DP · the reduction to problem #01
RUNTIME 9:43
AFTER THIS → 3 DRILLS · PROBLEM #02
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
17 / DRILL UNIT 02 · EQUAL HALVES · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Before any DP runs, one cheap check settles a large fraction of inputs. What is it?

Two subsets of equal sum each hold total/2. If the total is odd there is no such integer and the answer is false with no table at all. Reaching for the recurrence before the parity check is the giveaway that the reduction was never really understood.

DRILL 02 · RECALL

Once the sum is even, what is left to compute?

It is problem #01 with one specific target. The whole lecture is nine minutes because there is no new recurrence in it — this is the deck's first pure reduction, and recognising a solved problem underneath a new statement is the skill being drilled.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
18 / DRILL UNIT 02 · EQUAL HALVES · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · TRANSFER

Array [1, 5, 11, 5]. Does an equal partition exist?

Total is 22, half is 11, and {11} reaches it exactly. The tempting wrong answer is the last one: {1,11} is 12 and {5,5} is 10, which are not equal — a reminder that the check is 'does SOME subset hit total/2', not 'can I eyeball two groups that look close'.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
19 / PROBLEM #02 · REACH · HARD

Partition equal subset sum

HARD reach ▶ SOLVE ON LEETCODESheet says HARD, LeetCode says Medium. The sheet's label wins — and the gap is fair: on LeetCode you are told it is a DP problem by the tag, whereas the work here is spotting that an equal partition IS a subset-sum for half the total.
SIGNAL — WHAT GIVES IT AWAY

“Two subsets with equal sum.” Equal halves of a fixed total is a target in disguise — and one cheap arithmetic check settles a large share of inputs before any table exists.

INTUITION

If the array splits into two equal halves, each sums to total/2. So the question is only whether SOME subset reaches total/2 — which is the previous problem with one specific target. An odd total makes total/2 a non-integer, so the answer is false immediately.

STEPS
  1. Sum the array. If the total is odd, return false — no table required.
  2. Otherwise run subset sum for target = total / 2.
  3. Return its answer. There is no new recurrence in this problem.
BRUTEO(2ⁿ)
OPTIMALO(n·T)
↕ SCROLL
bool canPartition(vector<int>& a) {
    int total = accumulate(a.begin(), a.end(), 0);
    if (total % 2) return false;           // odd total: no table needed
    return subsetSum(a, total / 2);        // problem #01, one target
}
TIMEO(n·T)T is total/2 here
SPACEO(T)one row, rolled
TRAP

Reaching for the recurrence before the parity check. It is not a performance problem — it is a sign the reduction was never seen, and the same reflex will miss the guards on problem #05 where skipping them produces a wrong answer rather than a slow one.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
20 / INTRO UNIT 03 · MINIMISE THE GAP

UNIT 03 — MINIMISE THE GAP

Reading a minimum out of a table you already filled

THE QUESTION THIS LECTURE ANSWERS

SPLIT THE ARRAY IN TWO SO THE DIFFERENCE OF THE SUMS IS AS SMALL AS POSSIBLE.

last-row scancomplementary suminclusive bound
WHAT TO WATCH FOR
  • 01THE TABLE IS UNCHANGED — ONLY THE QUESTION ASKED OF ITS LAST ROW CHANGES
  • 02IF ONE SIDE IS s, THE OTHER IS total − s, SO THE GAP IS |total − 2s|
  • 03THE SECOND DIMENSION IS total + 1, BECAUSE TARGETS RUN 0..total INCLUSIVE
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
21 / VIDEO UNIT 03 · MINIMISE THE GAP

DP 16 · Partition Into Two Subsets, Minimum Difference

STRIVER A2Z
Scanning the last row · |total − 2s| · the target+1 dimension
RUNTIME 29:50
AFTER THIS → 3 DRILLS · PROBLEM #03
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
22 / DRILL UNIT 03 · MINIMISE THE GAP · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

The table is the same subset-sum table. What do you do with its LAST ROW to get the answer?

The last row says which sums a subset can reach. If one part sums to s, the other is total − s, so the gap is |total − 2s| — scan the row and take the best. The table was never rebuilt; only the question asked of it changed.

DRILL 02 · RECALL

The dp array's second dimension is declared with size total + 1. What breaks if it is total?

Targets run 0 to total INCLUSIVE, so there are total + 1 of them. It is the same off-by-one as deck I's n+1, one dimension over, and the lecture stops on it for exactly that reason.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
23 / DRILL UNIT 03 · MINIMISE THE GAP · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · TRANSFER

Array [1, 6, 11, 5], total 23. What is the minimum achievable difference?

{1, 5, 6} is 12 and {11} is 11, so the gap is 1. With an odd total, 0 is impossible — which is the parity check from the previous unit reappearing as a sanity bound rather than as an early exit.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
24 / PROBLEM #03 · REACH · HARD

Partition a set into two subsets with minimum absolute sum difference

HARD reach ▶ SOLVE ON GEEKSFORGEEKSThe sheet's own practice link for this row points at LeetCode 2035, which is a DIFFERENT problem: it requires the two halves to contain exactly n elements each, which turns it into a meet-in-the-middle problem rather than this one. The GeeksforGeeks link above is Striver's actual problem — any split, minimise the difference. Solve that one.
SIGNAL — WHAT GIVES IT AWAY

“Minimum absolute difference between the two parts.” An optimisation over every possible split — but the split is still just a subset, so the table does not change at all.

INTUITION

Fill the ordinary subset-sum table over targets 0..total. Its last row tells you every sum a subset can reach. If one side reaches s the other must be total − s, so the gap is |total − 2s| — scan the row and keep the best. The DP answered a reachability question; the minimisation happens afterwards, in a loop.

STEPS
  1. Compute the total, and fill subset sum for every target from 0 to total.
  2. Walk s from 0 to total/2 and keep the reachable s minimising |total − 2s|.
  3. Half the range suffices — s and total − s give the same difference.
  4. Size the second dimension total + 1: targets run 0..total inclusive.
BRUTEO(2ⁿ)
OPTIMALO(n·total)
↕ SCROLL
int minSubsetSumDifference(vector<int>& a) {
    int n = a.size(), total = accumulate(a.begin(), a.end(), 0);
    vector<bool> prev(total + 1, false), cur(total + 1, false);
    prev[0] = true;
    if (a[0] <= total) prev[a[0]] = true;
    for (int i = 1; i < n; i++) {
        cur[0] = true;
        for (int t = 1; t <= total; t++)
            cur[t] = prev[t] || (a[i] <= t ? prev[t - a[i]] : false);
        prev = cur;
    }
    int best = INT_MAX;                    // now just READ the last row
    for (int s = 0; s <= total / 2; s++)
        if (prev[s]) best = min(best, abs(total - 2 * s));
    return best;
}
TIMEO(n·total)one cell per index and reachable sum
SPACEO(total)one row, rolled
TRAP

Sizing the table `total` instead of `total + 1`. The sum `total` itself is a legal, reachable state — take everything — so the last column is a real answer, not padding. Same off-by-one as deck I's n+1, one dimension along.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
25 / INTRO UNIT 04 · COUNT, DON'T CHECK

UNIT 04 — COUNT, DON'T CHECK

Counting instead of checking — the same cells, added

THE QUESTION THIS LECTURE ANSWERS

HOW MANY DIFFERENT SUBSETS ADD UP TO EXACTLY K?

disjoint branchescounting DPthe zero case
WHAT TO WATCH FOR
  • 01THE TWO BRANCHES ARE DISJOINT, WHICH IS WHY THEY SIMPLY ADD
  • 02HE ADMITS A BUG IN LECTURE 14: THE BASE CASE INDEXED dp WITH a[0] UNGUARDED
  • 03POSITIVE INTEGERS IS STRESSED — A ZERO WOULD DOUBLE THE COUNT
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
26 / VIDEO UNIT 04 · COUNT, DON'T CHECK

DP 17 · Count Subsets with Sum K

STRIVER A2Z
OR becomes ADD · the a[0] ≤ s guard he missed in DP 14 · the zero case
RUNTIME 36:57
AFTER THIS → 3 DRILLS · PROBLEM #04
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
27 / DRILL UNIT 04 · COUNT, DON'T CHECK · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Reachability OR-ed the two branches. What does counting do with them?

Pick and not-pick partition the possibilities — no subset both takes and skips an element — so their counts are disjoint and simply add. Identical recursion, one operator different: that is the deck's whole thesis in a single line.

DRILL 02 · RECALL

The lecture admits an error in DP 14 and fixes it here. What was missing?

The base case writes at index a[0], and if a[0] is bigger than the declared target the write runs off the end — a runtime error he says he missed in the earlier lecture. Worth knowing both because it is a real bug and because it shows the base case indexes the table just as the loop does.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
28 / DRILL UNIT 04 · COUNT, DON'T CHECK · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

The constraints are stressed as POSITIVE integers. What goes wrong the moment a zero is allowed?

A zero costs nothing, so taking it and skipping it are two DIFFERENT subsets with the same sum. The base case that returns 1 has to return 2 when a[0] is 0. Harmless when checking reachability, quietly halving your answer when counting.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
29 / MECHANISM UNIT 04 · COUNTSUBSETS · CODE MIRRORED

ONE OPERATOR APART FROM THE LAST ONE

[1, 2, 2, 3], target 3. Identical recursion, identical table, identical pair of source cells. The two branches are added instead of OR-ed. Watch the base row: a zero in the array would make it 2 rather than 1, which is harmless when checking and halves your answer when counting.

RECURRENCE
STATE
CODE MIRROR
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
30 / PROBLEM #04 · COUNT · HARD

Count subsets with sum K

HARD count ▶ SOLVE ON GEEKSFORGEEKS
SIGNAL — WHAT GIVES IT AWAY

“How many subsets…” rather than “is there a subset…”. One word changes, and only the operator joining the two branches changes with it.

INTUITION

Pick and not-pick can never produce the same subset — one contains a[i] and the other does not — so the two counts are disjoint and simply add. Everything else, the state, the sources, the loop, is identical to problem #01. The one genuinely new hazard is a zero: it costs nothing, so taking it and skipping it are two different subsets with the same sum.

STEPS
  1. State: f(i, t) = how many subsets of a[0..i] sum to exactly t.
  2. Transition: f(i-1, t) + f(i-1, t - a[i]), the second only when a[i] <= t.
  3. Base row: 1 way at t = 0 — but 2 if a[0] is itself 0.
  4. Guard the base case's write: only index a[0] when a[0] <= K.
  5. Take the answer modulo 1e9+7 when the judge asks for it.
BRUTEO(2ⁿ)
OPTIMALO(n·K)
↕ SCROLL
int countSubsets(vector<int>& a, int K) {
    int n = a.size();
    vector<int> prev(K + 1, 0), cur(K + 1, 0);
    prev[0] = (a[0] == 0) ? 2 : 1;         // a zero is in OR out: two subsets
    if (a[0] != 0 && a[0] <= K) prev[a[0]] = 1;
    for (int i = 1; i < n; i++) {
        for (int t = 0; t <= K; t++) {
            int notPick = prev[t];
            int pick = (a[i] <= t) ? prev[t - a[i]] : 0;
            cur[t] = notPick + pick;       // ADD: the branches are disjoint
        }
        prev = cur;
    }
    return prev[K];
}
TIMEO(n·K)one cell per index and target
SPACEO(K)one row, rolled
TRAP

Assuming the base case returns 1. If a[0] is 0 there are TWO subsets summing to zero — {} and {0} — so it returns 2. On arrays of positive integers the bug never fires, which is exactly why it survives until a later problem loosens the constraints and halves your answer.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
31 / INTRO UNIT 05 · GIVEN DIFFERENCE

UNIT 05 — GIVEN DIFFERENCE

Algebra first, then a count you have already written

THE QUESTION THIS LECTURE ANSWERS

HOW MANY WAYS ARE THERE TO SPLIT THE ARRAY SO THE SUMS DIFFER BY EXACTLY D?

reductionguardsconstraints that hide a bug
WHAT TO WATCH FOR
  • 01TWO LINES OF ALGEBRA REPLACE AN ENTIRE RECURRENCE: (total − D) / 2
  • 02GUARD IT NON-NEGATIVE AND EVEN BEFORE THE TABLE IS TOUCHED
  • 03HE SHOWS THE PREVIOUS SOLUTION FAILING, AND THE CAUSE IS ZEROS
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
32 / VIDEO UNIT 05 · GIVEN DIFFERENCE

DP 18 · Count Partitions With Given Difference

STRIVER A2Z
S2 = (total − D)/2 · the two guards · why the earlier code failed on zeros
RUNTIME 18:00
AFTER THIS → 3 DRILLS · PROBLEM #05
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
33 / DRILL UNIT 05 · GIVEN DIFFERENCE · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Partitions into S1 and S2 with S1 − S2 = D. What target does this reduce to counting subsets for?

S1 + S2 = total and S1 − S2 = D, so S2 = (total − D)/2. Count the subsets summing to that and you have counted the partitions. Two lines of algebra replace an entirely new recurrence — and this is also what the next lecture reduces to.

DRILL 02 · RECALL

Two guards are needed on (total − D) before the DP runs. Which pair?

Negative means no such split exists; odd means the target is not an integer. Both return 0 immediately. Skip them and you either index the table with a negative number or silently truncate a fraction — a wrong count rather than a crash.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
34 / DRILL UNIT 05 · GIVEN DIFFERENCE · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

The lecture says the previous solution 'failed' on a test and explains why. What was the cause?

The earlier problem promised elements ≥ 1, so the zero case never arose; here the constraints allow 0 and each zero doubles the count. It is the same trap as the previous unit, now actually firing — which is why it is drilled twice.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
35 / PROBLEM #05 · COUNT · HARD

Count partitions with given difference

HARD count ▶ SOLVE ON GEEKSFORGEEKS
SIGNAL — WHAT GIVES IT AWAY

“Partitions whose sums differ by exactly D.” A constraint stated as a difference is almost always a subset-sum target after two lines of algebra.

INTUITION

Call the two sides S1 and S2. They satisfy S1 + S2 = total and S1 − S2 = D, so S2 = (total − D)/2. Counting the subsets that sum to that value counts the partitions. No new recurrence — but the derived target must be checked, because algebra happily produces negative and fractional answers that a table index cannot.

STEPS
  1. Compute total. Derive the target as (total − D) / 2.
  2. If total − D is negative, return 0 — no such split exists.
  3. If total − D is odd, return 0 — the target is not an integer.
  4. Otherwise count subsets summing to that target, using problem #04.
  5. Remember the zero case: this problem's constraints ALLOW zeros.
BRUTEO(2ⁿ)
OPTIMALO(n·T)
↕ SCROLL
int countPartitions(vector<int>& a, int D) {
    int total = accumulate(a.begin(), a.end(), 0);
    if (total - D < 0) return 0;           // no such split exists
    if ((total - D) % 2) return 0;         // target would not be an integer
    return countSubsets(a, (total - D) / 2);
}
TIMEO(n·T)T is the derived target
SPACEO(T)one row, rolled
TRAP

Deriving the target and using it unguarded. A negative value indexes the table out of range; an odd one silently truncates to the wrong target and returns a confident count for a question nobody asked.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
36 / INTRO UNIT 06 · 0/1 KNAPSACK

UNIT 06 — 0/1 KNAPSACK

The problem every other row in this deck is a disguise of

THE QUESTION THIS LECTURE ANSWERS

A THIEF WITH A BAG OF CAPACITY W. WHICH ITEMS MAXIMISE THE VALUE CARRIED?

knapsackcapacity0/1counterexample
WHAT TO WATCH FOR
  • 01HE DISPROVES GREEDY WITH AN n = 3 COUNTEREXAMPLE RATHER THAN ASSERTING IT
  • 02THE STATE IS INDEX AND CAPACITY — VALUE IS THE ANSWER, NEVER PART OF IT
  • 03“VERY VERY VERY VERY IMPORTANT FOR ANY INTERVIEW” — HIS WORDS
  • 04TAKE READS THE ROW ABOVE, WHICH IS WHAT MAKES EACH ITEM SINGLE-USE
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
37 / VIDEO UNIT 06 · 0/1 KNAPSACK

DP 19 · 0/1 Knapsack

STRIVER A2Z
Why greedy fails, proved · index and capacity as the state · take moves up a row
RUNTIME 41:19
AFTER THIS → 3 DRILLS · CONCEPT UNIT
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
38 / DRILL UNIT 06 · 0/1 KNAPSACK · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

The lecture spends real time proving one approach does NOT work. Which?

Greedy by value fails because a heavy expensive item can block two lighter ones worth more together; greedy by value-per-weight fails too once items cannot be split. He builds an n = 3 counterexample rather than asserting it, and that habit — disprove the cheap idea before writing the expensive one — is worth as much as the recurrence.

DRILL 02 · RECALL

What are the two indices of the knapsack table?

Index says which items are still on the table; capacity says how much room is left. Value is what you are MAXIMISING, never part of the state — putting it in the state is the classic beginner error and it makes the table unbounded.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
39 / DRILL UNIT 06 · 0/1 KNAPSACK · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why is this lecture in a deck whose sheet section never mentions knapsack?

Subset sum is knapsack with value = weight and a boolean answer. Coin change is unbounded knapsack. Rod cutting is unbounded knapsack with length as weight. Learning it once and recognising it five times is far cheaper than learning five problems.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
40 / MECHANISM UNIT 06 · KNAPSACK01 · CODE MIRRORED

THE PROBLEM EVERY OTHER ROW IS WEARING

Weights [1, 3, 4, 5], values [1, 4, 5, 7], capacity 7. Greedy by value takes the 7 and is beaten. The table takes the max of skip and take, and TAKE reads the row above, which is precisely what makes each item usable once.

RECURRENCE
STATE
CODE MIRROR
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
41 / INTRO UNIT 07 · WHEN GREEDY LIES

UNIT 07 — WHEN GREEDY LIES

Where the greedy instinct is right, and where the same instinct is wrong

THE QUESTION THIS LECTURE ANSWERS

WHEN CAN YOU JUST TAKE THE BIGGEST THING AVAILABLE, AND WHEN DOES THAT LOSE?

greedycounterexampleinfinite supplymin identity
WHAT TO WATCH FOR
  • 01THE COUNTEREXAMPLE: COINS {9, 6, 5, 1} AND A TARGET OF 11
  • 02GREEDY SPENDS THREE COINS; THE TABLE FINDS 6 + 5
  • 03INFINITE SUPPLY MEANS TAKE DOES NOT MOVE BACK — HE CALLS IT THE THUMB RULE
  • 04AN UNREACHABLE TARGET RETURNS A LARGE VALUE, NEVER 0
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
42 / VIDEO UNIT 07 · WHEN GREEDY LIES

DP 20 · Minimum Coins

STRIVER A2Z
Assign Cookies (greedy works) · Minimum Coins (greedy loses on {9,6,5,1})
RUNTIME 34:15
AFTER THIS → 3 DRILLS · PROBLEM #06, #07
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
43 / DRILL UNIT 07 · WHEN GREEDY LIES · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · TRACE

Coins {9, 6, 5, 1}, target 11 — the lecture's own counterexample. How many coins does GREEDY use, and how many is optimal?

Greedy grabs 9, is left owing 2, and can only pay it with two 1s — three coins. Taking NO large coin at all gives 6 + 5 = 11 in two. This is the whole reason coin change is a DP problem, and these are the exact denominations from the lecture.

DRILL 02 · RECALL

Coins may be reused without limit. What does the TAKE branch do to the index?

He calls this the thumb rule and repeats it: with infinite supply you do not move back after taking, because the same coin is still available. One character of difference from 0/1 knapsack, and it is the only difference.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
44 / DRILL UNIT 07 · WHEN GREEDY LIES · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

A target that no combination of coins can reach must return what from the base case?

This is a MIN, so an impossible state must be unattractive — the same identity argument as deck I's out-of-grid cells. Return 0 and impossibility becomes the cheapest option and wins. Most implementations use a large sentinel and convert it to −1 only at the very end.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
45 / MECHANISM UNIT 07 · GREEDYFAIL · CODE MIRRORED

WHERE GREEDY IS PROVABLY WRONG

Striver's own counterexample: coins {9, 6, 5, 1}, target 11. Greedy grabs the 9, owes 2, and pays it with two 1s. three coins. The table finds 6 + 5. Step to the last cell and watch the two disagree; the cells where they differ are tinted.

RECURRENCE
STATE
CODE MIRROR
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
46 / PROBLEM #06 · OPTIMISE · EASY

Assign Cookies

EASY optimise ▶ SOLVE ON LEETCODEThis row is not dynamic programming at all. It is a greedy two-pointer problem, and it is the only EASY row in a section of HARDs. It sits in unit 07 next to Minimum Coins on purpose: here the greedy choice is provably optimal, and one slide later the same instinct is provably wrong. Knowing which you are looking at is the actual skill.
SIGNAL — WHAT GIVES IT AWAY

“Assign each cookie to at most one child.” Note what is NOT here: no target, no subset, no counting. This row is not dynamic programming — it is greedy, and it is in this deck as the control case.

INTUITION

Sort both lists. Offer the smallest cookie to the least greedy child: if it fits, that is a child satisfied with the cheapest possible cookie, and no better use of that cookie exists. If it does not fit, no child can use it, so discard it. Every step is provably safe — which is precisely what the next problem's greedy step is not.

STEPS
  1. Sort the greed factors and the cookie sizes.
  2. Walk both with two pointers, smallest first.
  3. If the current cookie satisfies the current child, count them and advance both.
  4. Otherwise advance only the cookie — it is too small for anyone remaining.
  5. The count of satisfied children is the answer.
BRUTEO(n·m)
OPTIMALO(n log n + m log m)
↕ SCROLL
int findContentChildren(vector<int>& g, vector<int>& s) {
    sort(g.begin(), g.end());              // greediest child last
    sort(s.begin(), s.end());              // biggest cookie last
    int child = 0, cookie = 0;
    while (child < (int)g.size() && cookie < (int)s.size()) {
        if (s[cookie] >= g[child]) child++;   // this cookie satisfies them
        cookie++;                             // either way, cookie is spent
    }
    return child;
}
TIMEO(n log n + m log m)dominated by the two sorts
SPACEO(1)two pointers, nothing stored
TRAP

Concluding from this problem that greedy is generally safe on 'take the best fit' questions. It is safe HERE because the exchange argument holds — swapping in a larger cookie never helps. One slide on, with coins {9, 6, 5, 1}, the same reasoning produces a wrong answer.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
47 / PROBLEM #07 · INFINITE · HARD

Minimum Coins (DP - 20)

HARD infinite ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Fewest coins to make an amount”, with unlimited coins of each denomination. Minimisation plus infinite supply — and the obvious greedy answer is wrong.

INTUITION

Greedy takes the biggest coin that fits, and on {9, 6, 5, 1} with target 11 it grabs the 9, owes 2, and pays with two 1s — three coins. Taking no large coin at all gives 6 + 5. So every coin has to be tried at every target, which is the DP. Because supply is unlimited, taking a coin does NOT move past it.

STEPS
  1. State: f(t) = fewest coins summing to exactly t.
  2. Transition: min over every coin c ≤ t of 1 + f(t − c).
  3. Base: f(0) = 0. Everything else starts at a large sentinel.
  4. Unreachable targets keep the sentinel — convert to −1 only at the very end.
  5. Iterate targets upward; f(t − c) is already final when f(t) is computed.
BRUTEO(Tⁿ)
OPTIMALO(n·T)
↕ SCROLL
int coinChange(vector<int>& coins, int T) {
    const int BIG = 1e9;                   // BIG, never 0: this is a MIN
    vector<int> dp(T + 1, BIG);
    dp[0] = 0;
    for (int t = 1; t <= T; t++)
        for (int c : coins)                // EVERY coin, not the biggest
            if (c <= t && dp[t - c] + 1 < dp[t])
                dp[t] = dp[t - c] + 1;
    return dp[T] >= BIG ? -1 : dp[T];
}
TIMEO(n·T)every coin tried at every target
SPACEO(T)one array of T+1
TRAP

Initialising unreachable targets to 0 rather than a large value. The min then prefers the impossible option at every step and reports a confident, small, wrong number. Same identity argument as deck I's out-of-grid cells.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
48 / INTRO UNIT 08 · SIGNS ARE A PARTITION

UNIT 08 — SIGNS ARE A PARTITION

Signs are a partition wearing different notation

THE QUESTION THIS LECTURE ANSWERS

ASSIGN A + OR A − TO EVERY ELEMENT. HOW MANY ASSIGNMENTS REACH THE TARGET?

reductionsign assignmentpartition
WHAT TO WATCH FOR
  • 01THE PLUS-SIGNED ELEMENTS ARE ONE SUBSET AND THE MINUS-SIGNED ONES THE OTHER
  • 02SO THEIR SUMS DIFFER BY THE TARGET — THIS IS PROBLEM #05 EXACTLY
  • 03EVERY ELEMENT MUST BE SIGNED, WHICH IS WHAT PARTITION ALREADY MEANS
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
49 / VIDEO UNIT 08 · SIGNS ARE A PARTITION

DP 21 · Target Sum

STRIVER A2Z
The reduction to counting partitions with a given difference
RUNTIME 9:04
AFTER THIS → 3 DRILLS · PROBLEM #08
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
50 / DRILL UNIT 08 · SIGNS ARE A PARTITION · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Assigning + or − to every element, counting the ways to hit a target. What is this the same as?

The plus-signed elements form one subset and the minus-signed ones the other, so their sums differ by exactly the target. It is #05 with the statement rewritten, which is why the lecture is nine minutes long and writes no new recurrence.

DRILL 02 · RECALL

Every element must get a sign — none may be left out. Does that break the reduction?

A partition already assigns every element to exactly one side, so 'must be signed' and 'must be partitioned' are the same requirement. Seeing that the constraint is already satisfied — rather than adding machinery for it — is the reduction working.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
51 / DRILL UNIT 08 · SIGNS ARE A PARTITION · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · TRANSFER

Array [1, 2, 3, 1], target 3, as in the lecture. How many sign assignments work?

Two: −1+2+3−1 and +1−2+3+1. Total is 7 and the target is 3, so the negative side must sum to (7−3)/2 = 2 — reachable as {2} or as {1,1}, which is exactly two subsets. The reduction gives the count without enumerating signs at all.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
52 / PROBLEM #08 · COUNT · HARD

Target sum

HARD count ▶ SOLVE ON LEETCODESolved by REDUCTION, not by a new recurrence: assigning + and - signs to reach a target is exactly problem #05, counting partitions with a given difference. The work is seeing that, which is why the sheet calls it HARD and LeetCode calls it Medium.
SIGNAL — WHAT GIVES IT AWAY

“Assign + or − to every element to reach a target.” Two groups, every element in exactly one, a constraint on the difference of their sums. That is a partition with a given difference, written in other notation.

INTUITION

The plus-signed elements form one subset and the minus-signed ones the other. Their sums differ by exactly the target, so counting valid sign assignments is counting partitions with difference = target — problem #05, unchanged. The 'every element must be signed' requirement is not an extra constraint: a partition already puts every element on one side.

STEPS
  1. Recognise the reduction: signs are a partition, the target is the difference.
  2. Derive the target as (total − |target|) / 2.
  3. Guard it non-negative and even, exactly as in problem #05.
  4. Count subsets summing to it — including the zero case, which fires here.
BRUTEO(2ⁿ)
OPTIMALO(n·T)
↕ SCROLL
int findTargetSumWays(vector<int>& a, int target) {
    // + signed elements and - signed elements are two subsets whose sums
    // differ by `target`. That is problem #05, verbatim.
    int total = accumulate(a.begin(), a.end(), 0);
    if (total - abs(target) < 0) return 0;
    if ((total - abs(target)) % 2) return 0;
    return countSubsets(a, (total - abs(target)) / 2);
}
TIMEO(n·T)the derived subset-sum target
SPACEO(T)one row, rolled
TRAP

Writing a fresh recurrence over signs. It works, it is slower to derive, and it duplicates code you already have. The expensive mistake here is not a bug — it is failing to notice that the problem is already solved.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
53 / INTRO UNIT 09 · COUNT WITH REFILLS

UNIT 09 — COUNT WITH REFILLS

Counting with unlimited supply, and why order never double-counts

THE QUESTION THIS LECTURE ANSWERS

HOW MANY COMBINATIONS OF COINS ADD UP TO THE AMOUNT, REUSE ALLOWED?

combinations vs permutationsinfinite supplyguarded branch
WHAT TO WATCH FOR
  • 01THE BASE CASE FLIPS FROM A LARGE SENTINEL TO A 1: PAYING EXACTLY IS ONE WAY
  • 02NOT-PICK MOVES PERMANENTLY PAST A COIN, SO EACH COMBINATION APPEARS ONCE
  • 03THE TAKE BRANCH IS GUARDED BY a[i] ≤ TARGET BEFORE IT RUNS
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
54 / VIDEO UNIT 09 · COUNT WITH REFILLS

DP 22 · Coin Change 2

STRIVER A2Z
ADD not MIN · base case 1 · why this counts combinations, not permutations
RUNTIME 22:17
AFTER THIS → 3 DRILLS · PROBLEM #09
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
55 / DRILL UNIT 09 · COUNT WITH REFILLS · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Coin Change 2 counts combinations rather than minimising coins. Which two things change from #07?

Same table, same infinite-supply take branch, min swapped for a sum — and the base case flips from a large sentinel to a 1, because paying the target exactly IS one valid combination. Deck I's stairs-versus-frog contrast, one dimension up.

DRILL 02 · RECALL

Why does this count COMBINATIONS rather than permutations — why is 1+2 not counted separately from 2+1?

Not-pick moves permanently past a coin, so every combination is generated in exactly one index order. Swap the loop order in the tabulated version and you start counting permutations instead — the classic coin-change trap, and it is a difference in the ITERATION, not in the recurrence.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
56 / DRILL UNIT 09 · COUNT WITH REFILLS · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

The take branch is guarded before it runs. What does the guard check?

Taking a coin larger than what you still owe would drive the target negative and index the table out of range. He states it as a condition on the take branch rather than as a base case — cheaper, and it keeps the base case about the array rather than about the target.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
57 / PROBLEM #09 · INFINITE · HARD

Coin Change 2 (DP - 22)

HARD infinite ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“How many combinations of coins make the amount”, unlimited supply. Counting plus infinite supply — problem #07's table with the operator swapped.

INTUITION

Same states, same infinite-supply take branch, but min becomes a sum and the base case becomes 1: paying the amount exactly IS one combination. The subtlety is ordering — because not-pick moves permanently past a coin, each combination is generated in exactly one coin order, so 1+2 and 2+1 are never counted twice.

STEPS
  1. State: f(i, t) = combinations from coins i.. summing to t.
  2. Transition: f(i+1, t) + f(i, t − coins[i]) — take stays on the same coin.
  3. Base: f(·, 0) = 1. Paying exactly is one valid combination.
  4. Tabulate with coins in the OUTER loop and targets inner, forward.
  5. Use a 64-bit accumulator: combination counts overflow int quickly.
BRUTEO(Tⁿ)
OPTIMALO(n·T)
↕ SCROLL
int change(int amount, vector<int>& coins) {
    vector<long long> dp(amount + 1, 0);
    dp[0] = 1;                             // paying exactly IS one combination
    for (int c : coins)                    // coins OUTSIDE: combinations
        for (int t = c; t <= amount; t++)  // targets INSIDE, forward
            dp[t] += dp[t - c];            // take stays on the same coin
    return (int)dp[amount];
}
TIMEO(n·T)one cell per coin and target
SPACEO(T)one array of T+1
TRAP

Swapping the loops. Targets outside and coins inside counts PERMUTATIONS — 1+2 and 2+1 become two answers — and the number returned is larger and entirely believable. The recurrence on paper is unchanged, which is what makes it hard to spot.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
58 / INTRO UNIT 10 · UNBOUNDED KNAPSACK

UNIT 10 — UNBOUNDED KNAPSACK

0/1 knapsack with one index changed

THE QUESTION THIS LECTURE ANSWERS

THE SAME BAG, BUT NOW EVERY ITEM HAS UNLIMITED COPIES. WHAT CHANGES?

unboundedsame-index takeforward iteration
WHAT TO WATCH FOR
  • 01HE NAMES DP 19 AS THE PREREQUISITE IN THE FIRST MINUTE
  • 02THE WHOLE DIFFERENCE: TAKE READS dp[i], NOT dp[i−1]
  • 03ONE ROW ITERATED FORWARD IS CORRECT HERE — THE 0/1 VERSION IS THE FUSSY ONE
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
59 / VIDEO UNIT 10 · UNBOUNDED KNAPSACK

DP 23 · Unbounded Knapsack

STRIVER A2Z
The single-index difference · why it space-optimises to one forward row
RUNTIME 22:54
AFTER THIS → 3 DRILLS · PROBLEM #10
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
60 / DRILL UNIT 10 · UNBOUNDED KNAPSACK · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

The lecture names its prerequisite in the first minute. What is it?

He says to go and watch 0/1 knapsack before this one, because unbounded knapsack is defined as the difference from it. That is also why this deck keeps DP 19 despite the sheet having no row for it — the lecture after it does not stand alone.

DRILL 02 · RECALL

State the entire difference between 0/1 and unbounded knapsack.

That is genuinely all of it. Same table, same capacity index, same max — one branch does not decrement the index. Being able to state a difference this precisely is what makes the family collapse into one thing you actually remember.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
61 / DRILL UNIT 10 · UNBOUNDED KNAPSACK · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

0/1 knapsack over n items space-optimises to a single row. Does unbounded?

Because the take branch stays at the same index, the row you are writing is the row you want to read — so a single array iterated in increasing capacity is correct, and it is the 0/1 version that has to be careful about direction. The one place unbounded is the easier of the two.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
62 / MECHANISM UNIT 10 · UNBOUNDED · CODE MIRRORED

ONE INDEX, AND THE ITEM COMES BACK

Weights [2, 4, 6], values [5, 11, 13], capacity 10. The entire difference from 0/1: the TAKE branch reads its own row instead of the one above, so the item it just used is still available. Watch the source cell stay level rather than rising.

RECURRENCE
STATE
CODE MIRROR
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
63 / PROBLEM #10 · INFINITE · HARD

Unbounded knapsack

HARD infinite ▶ SOLVE ON GEEKSFORGEEKS
SIGNAL — WHAT GIVES IT AWAY

A knapsack statement with the words “infinite supply” or “any number of times” in it. Everything else about the problem is 0/1 knapsack.

INTUITION

In 0/1 knapsack the take branch moves to the previous item, which is what makes each item single-use. Remove that one decrement and the item stays available. That is the complete difference — and it is also why the space-optimised version iterates capacity FORWARD, so the cell it reads has already been updated for this item.

STEPS
  1. State: f(i, c) = best value from items 0..i within capacity c.
  2. Transition: max(f(i-1, c), v[i] + f(i, c - w[i])) — note f(i), not f(i-1).
  3. Base row: item 0 fits floor(c / w[0]) times.
  4. Space-optimise to one array, iterated forward over capacity.
BRUTEO(Wⁿ)
OPTIMALO(n·W)
↕ SCROLL
int unboundedKnapsack(vector<int>& w, vector<int>& v, int W) {
    vector<int> dp(W + 1, 0);
    for (int i = 0; i < (int)w.size(); i++)
        for (int c = w[i]; c <= W; c++)    // FORWARD: dp[c-w[i]] is this row,
            dp[c] = max(dp[c],             // already updated, so the item
                        v[i] + dp[c - w[i]]);  // can be taken again
    return dp[W];
}
TIMEO(n·W)one cell per item and capacity
SPACEO(W)one array, forward-iterated
TRAP

Reading dp[i-1][c-w[i]] out of habit. Each item then gets used at most once, the total comes out lower, and nothing about the code looks wrong — it is the 0/1 solution answering an unbounded question.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
64 / INTRO UNIT 11 · ROD CUTTING

UNIT 11 — ROD CUTTING

Reframing a statement until a solved problem appears underneath it

THE QUESTION THIS LECTURE ANSWERS

CUT A ROD OF LENGTH n INTO PIECES TO MAXIMISE WHAT THE PIECES SELL FOR.

reframinglength as weightunbounded knapsack
WHAT TO WATCH FOR
  • 01HE TURNS “BREAK n INTO PIECES” INTO “COLLECT LENGTHS SUMMING TO n”
  • 02LENGTH IS THE WEIGHT, THE ROD IS THE CAPACITY, PRICE IS THE VALUE
  • 03NOTHING FORBIDS TWO PIECES OF THE SAME LENGTH — SO IT IS UNBOUNDED
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
65 / VIDEO UNIT 11 · ROD CUTTING

DP 24 · Rod Cutting Problem

STRIVER A2Z
Cutting reframed as collecting · length is weight · why lengths repeat
RUNTIME 22:54
AFTER THIS → 3 DRILLS · PROBLEM #11
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
66 / DRILL UNIT 11 · ROD CUTTING · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

The lecture reframes the statement before solving it. From what, to what?

Cutting is hard to write a recurrence for; COLLECTING is unbounded knapsack, which is already solved. Turning a problem around until it becomes one you have done is the single most transferable move in this deck.

DRILL 02 · RECALL

Mapped onto unbounded knapsack, what plays the part of the WEIGHT?

Length is what the rod's total capacity is spent on, so length is weight and the rod's length is the capacity; price is the value being maximised. Getting this mapping right is the entire problem — after it, the code is the previous lecture's.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
67 / DRILL UNIT 11 · ROD CUTTING · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why can a rod length be used more than once, making this unbounded rather than 0/1?

A rod of length 8 can perfectly well be cut into four pieces of length 2. There is no constraint saying each length is available once, so the take branch stays put — the same test you apply to every problem in this group.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
68 / PROBLEM #11 · INFINITE · HARD

Rod Cutting Problem | (DP - 24)

HARD infinite ▶ SOLVE ON GEEKSFORGEEKS
SIGNAL — WHAT GIVES IT AWAY

“Cut a rod into pieces to maximise the total price.” Cutting is hard to recurse on directly — but the pieces have to sum to the rod's length, and that is a capacity.

INTUITION

Turn it around: instead of breaking a rod of length n, COLLECT piece lengths that sum to n, maximising their total price. Now length is weight, the rod's length is the capacity, and price is value — unbounded knapsack, because nothing stops you cutting two pieces the same length. There is no new code to write once the statement has been reframed.

STEPS
  1. Reframe: collect lengths summing to n rather than cutting n into pieces.
  2. Map it: length is the weight, n is the capacity, price is the value.
  3. Note that lengths may repeat, so this is unbounded, not 0/1.
  4. Run unbounded knapsack over lengths 1..n with price[length-1].
  5. Answer is dp[n].
BRUTEO(2ⁿ)
OPTIMALO(n²)
↕ SCROLL
int cutRod(vector<int>& price, int n) {
    // length is the WEIGHT, the rod is the CAPACITY, price is the VALUE.
    // A length may be cut more than once, so this is UNBOUNDED knapsack.
    vector<int> dp(n + 1, 0);
    for (int len = 1; len <= n; len++)
        for (int c = len; c <= n; c++)
            dp[c] = max(dp[c], price[len - 1] + dp[c - len]);
    return dp[n];
}
TIMEO(n²)n lengths against n capacities
SPACEO(n)one array, forward-iterated
TRAP

Trying to recurse on the CUTS — 'cut here, then solve both halves'. It is correct and it is a partition DP, which is deck IV's material and far more work. The whole lesson of this lecture is that reframing the statement removes the difficulty instead of managing it.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
69 / RECALL RETRIEVAL, NOT RECOGNITION · 1 OF 3

NAME THE OPERATOR FROM WHAT IS ASKED

DRILL 01 · RECALL

Same table, same two source cells. Which operator answers 'how many ways'?

Add. Pick and not-pick are disjoint — no subset both takes and skips an element — so their counts sum. OR answers reachability, MIN or MAX answers optimisation, and the recursion underneath never changed.

DRILL 02 · RECALL

Which single change turns 0/1 knapsack into unbounded knapsack?

One index. dp[i-1][c-w[i]] becomes dp[i][c-w[i]], so the item you just used is still on the table. Being able to state the difference in one clause is what collapses coin change, unbounded knapsack and rod cutting into a single thing worth remembering.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
70 / RECALL RETRIEVAL, NOT RECOGNITION · 2 OF 3

NAME THE OPERATOR FROM WHAT IS ASKED

DRILL 01 · RECALL

Counting partitions whose sums differ by D. Which target do you count subsets for?

(total − D) / 2. The smaller side is S2, and S1 + S2 = total with S1 − S2 = D. Guard it twice before using it — negative means no such split exists, odd means it is not an integer — and Target Sum is this same reduction with the statement rewritten in plus and minus signs.

DRILL 02 · RECALL

A minimum-coins DP hits a target no combination can reach. The base case returns…

The identity for a min is +∞. Return 0 and impossibility becomes free and wins every comparison. Exactly the argument deck I made about out-of-grid cells — the value comes from the OPERATOR, and it does not care that this is coins rather than a grid.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
71 / RECALL RETRIEVAL, NOT RECOGNITION · 3 OF 3

NAME THE OPERATOR FROM WHAT IS ASKED

DRILL 01 · RECALL

Coin Change 2 counts combinations. What stops 1 + 2 and 2 + 1 counting as two?

Each combination is generated in exactly one index order. Once not-pick has moved past a coin it never comes back, so an ordering is never revisited. Swap the two loops in the tabulated version and you start counting permutations — a difference in the ITERATION, not in the recurrence.

DRILL 02 · RECALL

Rod cutting is unbounded knapsack. What plays the part of the weight?

Length is weight, the rod is the capacity, price is the value. The lecture turns 'break n into pieces' into 'collect lengths summing to n, maximising price' — and once it is phrased that way there is no new code to write. Reframing until a solved problem appears is the move worth stealing.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
72 / TRAPS PLAUSIBLE, WRONG, AND SILENT

SIX WAYS TO BE PLAUSIBLY WRONG

Four of these six return a NUMBER rather than an error, and three of them return the right number on the inputs you are most likely to try. A zero in the array, a swapped pair of loops, an index that should not have moved.

A BOOLEAN MEMO WITH NO SENTINEL

vector<vector<bool>> dp cannot say 'not computed yet', so every state either recomputes or returns a stale false. Use int and −1. The answer stays right and the runtime stays exponential, which is the worst combination.

ZEROS WHEN YOU ARE COUNTING

A zero can be taken or skipped, so each one DOUBLES the number of subsets. The base case owes 2, not 1. Invisible on arrays of positive integers, which is exactly where it hides until a later problem loosens the constraints.

0 FOR AN IMPOSSIBLE STATE IN A MIN

Minimum coins for an unreachable target must return a large value. Return 0 and the impossible option is free, wins every min, and the function reports a small number with total confidence.

REDUCING WITHOUT GUARDING THE TARGET

(total − D) / 2 must be non-negative AND even. Skip either check and you index the table with a negative number or silently truncate a half — a wrong count rather than a crash.

TAKE MOVING BACK WHEN SUPPLY IS INFINITE

dp[i-1][c-w[i]] in an unbounded problem lets each item be used once. The answer is still plausible, merely too small, and nothing about the code looks wrong.

SWAPPING THE LOOPS IN COIN CHANGE 2

Iterating targets outside and coins inside counts PERMUTATIONS: 1+2 and 2+1 become two answers. The number returned is larger and entirely believable, and the recurrence on paper is unchanged.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
73 / CHEATSHEET REVISION SURFACE

ONE SCREEN, THE WHOLE DECK

Eleven rows, one table. The right column is the transition — read down it and notice how little actually changes between them.

ALGORITHM
TIME
SPACE
REACH FOR IT WHEN
Subset sum · reachable?
O(n·T)
O(T)
dp[i-1][t] OR dp[i-1][t-a[i]]
Count subsets with sum K
O(n·T)
O(T)
same cells, ADDED; a[0]==0 gives base 2
Equal partition
O(n·T)
O(T)
total odd → false; else subset sum for total/2
Min abs sum difference
O(n·T)
O(T)
fill the row, scan it, minimise |total − 2s|
Count partitions, diff D
O(n·T)
O(T)
count subsets for (total−D)/2; guard ≥0 and even
Target sum (± signs)
O(n·T)
O(T)
identical to partitions with difference D
0/1 knapsack
O(n·W)
O(W)
max(skip, v[i] + dp[i-1][c-w[i]]) — take moves up
Unbounded knapsack
O(n·W)
O(W)
max(skip, v[i] + dp[i][c-w[i]]) — take stays
Minimum coins
O(n·T)
O(T)
min over coins of 1 + dp[t-c]; BIG for impossible
Coin change 2 · count
O(n·T)
O(T)
add the branches; base 1 at t==0, take stays
Rod cutting
O(n²)
O(n)
unbounded knapsack, length = weight, price = value
Greedy on coins
check {9,6,5,1} target 11 before trusting it: 3 vs 2
INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
74 / CLOSE STEP 16 · DECK 2 OF 4

PICK OR DON'T

Every problem here was the same two branches over the same table: take this element and reduce what you owe, or skip it and don't. OR them for reachability, ADD them to count, MIN or MAX them to optimise, and stop decrementing the index when supply is unlimited. The rest was algebra — equal halves, a given difference and a page of plus and minus signs all collapsed onto one subset-sum target. Deck III moves the second index onto a SECOND STRING, which is where the same table starts comparing two sequences instead of paying off a number.

00%
OF THIS DECK SOLVED
← ALL TOPICSTHE SHELFDECK I · FOUNDATIONS

Deck 2 of 4. Lectures DP 14-24 of 56. Eleven of the sheet's 55 rows. DP 19 (0/1 Knapsack) is a lecture with no sheet row and runs as a concept unit; Assign Cookies is a sheet row with no lecture and is not a DP problem — it sits beside Minimum Coins so the pair can be compared. Every drill cites its lecture transcript; every bench fills in numbers the build re-derived and asserted against brute force.

INVARIANT · DYNAMIC PROGRAMMING · SUBSEQUENCES · DECK 2 OF 4
■ TOO SMALL TO READ
1280×720
INVARIANT · STEP 16 · DECK 2 OF 4

This one needs a laptop

Not a preference — an arithmetic one. Every slide is a fixed 1280 × 720 stage: a graph animating beside the code that drives it, with the problems laid out two columns wide. It scales as a single piece, so on a screen this size the body text comes out around 4px tall.

Shrinking it further would not help, and rebuilding it to reflow would mean losing the thing that makes it worth reading.

YOUR SCREEN0 × 0
NEEDED1060 × 610
WHAT IS WAITING ON THE LAPTOP
PICK OR DON'T · THEN COUNT WHAT'S LEFT
Your progress is saved per device, so anything you tick on the laptop will be waiting there.