ЁЯПл The SchoolтА║ЁЯзо DSAтА║ЁЯза рдзрдбрд╛ 11 тАФ Dynamic programming рдЖрдгрд┐ greedy: рдлрд│реНрдпрд╛рд╡рд░рдЪреА рдЙрддреНрддрд░реЗ
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯза рдзрдбрд╛ 11 тАФ Dynamic programming рдЖрдгрд┐ greedy: рдлрд│реНрдпрд╛рд╡рд░рдЪреА рдЙрддреНрддрд░реЗ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 11 ┬╖ рдкреБрдвреЗ: lesson-12-choosing


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ11. exponential recursion рдЪреЗ linear table рдордзреНрдпреЗ рд░реВрдкрд╛рдВрддрд░, рдЪрд╛рд░ рдкрд╛рдпрд▒реНрдпрд╛рдВрдЪреА рдХреГрддреА, рдЖрдгрд┐ proof рд╕рд╛рдВрдЧреЗрд▓ рддреЗрд╡реНрд╣рд╛рдЪ рдЪрд╛рд▓рдгрд╛рд░рд╛ shortcut.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдореБрдЦреНрдпрд╛рдзреНрдпрд╛рдкрд┐рдХреЗрдиреЗ fib(25) 242,785 рд╡реЗрд│рд╛ рд╡рд┐рдЪрд╛рд░рд▓реЗ рд╣реЛрддреЗ, рдЖрдард╡рддреЗ? рдХреЙрд░рд┐рдбреЙрд░рдордзреНрдпреЗ рдПрдХ рдлрд│рд╛ рд▓рд╛рд╡рд╛: рдкрд╣рд┐рд▓реНрдпрд╛рдВрджрд╛ рдХреЛрдгреА "fib(17) = 1597" рдЕрд╕реЗ рдЙрддреНрддрд░ рджрд┐рд▓реЗ рдХреА рддреЗ рддрд┐рдереЗ рд▓рд┐рд╣рд╛. рдкреБрдврдЪреНрдпрд╛ рд╡реЗрд│реА рдЬреНрдпрд╛рд▓рд╛ рд╡рд┐рдЪрд╛рд░рд▓реЗ рдЬрд╛рдИрд▓ рддреЛ рддреНрдпрд╛рдРрд╡рдЬреА рдлрд│рд╛ рдкрд╛рд╣рддреЛ. рдЖрддрд╛ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╢реНрдирд╛рдЪреЗ рдЙрддреНрддрд░ рдПрдХрджрд╛рдЪ рдХрд╛рдврд▓реЗ рдЬрд╛рддреЗ: рдПрдХреВрдг 49. рд╣реЗ memoisation. рдХрд┐рдВрд╡рд╛ рдлрд│рд╛ рд╕реНрд╡рддрдГрдЪ рдЦрд╛рд▓реВрди рднрд░рд╛ тАФ fib(0), fib(1), fib(2)тАж тАФ рдХреЛрдгрд╛рд▓рд╛рд╣реА рди рд╡рд┐рдЪрд╛рд░рддрд╛: рд╣реЗ tabulation.

Greedy рд╡реЗрдЧрд│реЗ рдЖрд╣реЗ: рдлрд│рд╛ рдирд╛рд╣реА, рдкреНрд░рддреНрдпреЗрдХ рд╡реЗрд│реА рдлрдХреНрдд рд╕рд░реНрд╡рд╛рдд рдЪрд╛рдВрдЧрд▓реА рджрд┐рд╕рдгрд╛рд░реА рдкрд╛рдпрд░реА рдШреНрдпрд╛. рдПрдХрд╛ рдЦреЛрд▓реАрдд рдЬрд╛рд╕реНрддреАрдд рдЬрд╛рд╕реНрдд club meetings рдмрд╕рд╡рд╛рдпрд▓рд╛, рдиреЗрд╣рдореА рдЖрдзреА рд╕рдВрдкрдгрд╛рд░реА meeting рдШреНрдпрд╛. рд╣реЗ рдЪрд╛рд▓рддреЗ тАФ рддреНрдпрд╛рдЪрд╛ proof рдЖрд╣реЗ. 1, 3 рдЖрдгрд┐ 4 рдЪреНрдпрд╛ рдирд╛рдгреНрдпрд╛рдВрдиреА 6 рдмрдирд╡рддрд╛рдирд╛ "рдЖрдзреА рд╕рд░реНрд╡рд╛рдд рдореЛрдареЗ рдирд╛рдгреЗ" 4+1+1 = рддреАрди рдирд╛рдгреА рджреЗрддреЗ; рдлрд│рд╛ рд╕рд╛рдВрдЧрддреЛ 3+3 = рджреЛрди. рдХреЛрдгреАрддрд░реА рд╕рд┐рджреНрдз рдХреЗрд▓реЗ рдЕрд╕реЗрд▓ рддрд░рдЪ greedy рдмрд░реЛрдмрд░ рдЕрд╕рддреЛ.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
  S0["stairs(0)=1"] --> S1["stairs(1)=1"] --> S2["stairs(2)=2"] --> S3["stairs(3)=3"] --> S4["stairs(4)=5"] --> Sn["тАж stairs(n) = stairs(nтИТ1) + stairs(nтИТ2)"]

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

optimisation problems рдЪреНрдпрд╛ рдореЛрдареНрдпрд╛ рдХреБрдЯреБрдВрдмрд╛рд╕рд╛рдареА DP рдореНрд╣рдгрдЬреЗ "рд╕реЗрдХрдВрджрд╛рдВрдд рдЪрд╛рд▓рддреЗ" рдЖрдгрд┐ "рд╡рд┐рд╢реНрд╡рд╛рдЪрд╛ рдЕрдВрдд рд╣реЛрдИрдкрд░реНрдпрдВрддрд╣реА рд╕рдВрдкрдгрд╛рд░ рдирд╛рд╣реА" рдпрд╛рдВрддрд▓рд╛ рдлрд░рдХ; greedy рдореНрд╣рдгрдЬреЗ рд╣реБрд╢рд╛рд░ O(n log n) рдЖрдгрд┐ рдЪреБрдХреАрдЪреЗ рдЙрддреНрддрд░ рдпрд╛рдВрддрд▓рд╛ рдлрд░рдХ. рдХреЛрдгрддреЗ рдХреЛрдгрддреЗ рд╣реЗ рдУрд│рдЦрдгреЗ рд╣реЗрдЪ рдХреМрд╢рд▓реНрдп.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

fib_memo recursion рд▓рд╛ dict рдЬреЛрдбрддреЛ; stairs рджреЛрди variables рдлрд┐рд░рд╡рдд рдареЗрд╡рддреЛ; coin_change рд╕рд░реНрд╡реЛрддреНрддрдо counts рдЪреЗ table рднрд░рддреЛ рдЖрдгрд┐ рдЕрд╢рдХреНрдп рдЕрд╕реЗрд▓ рддрд░ тИТ1 рдкрд░рдд рдХрд░рддреЛ; max_meetings end time рдиреБрд╕рд╛рд░ sort рдХрд░рддреЛ рдЖрдгрд┐ рдЬреЗ рдмрд╕рддреЗ рддреЗ рдШреЗрддреЛ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 dsa/demo.py dp
python3 - <<'EOF'
import sys; sys.path.insert(0, "dsa"); from algorithms import coin_change
def greedy(coins, amount):
    n = 0
    for c in sorted(coins, reverse=True): n += amount // c; amount %= c
    return n if amount == 0 else -1
for coins, amt in (([1, 5, 10, 25], 63), ([1, 3, 4], 6), ([4, 7], 5)):
    print(coins, amt, "greedy:", greedy(coins, amt), "DP:", coin_change(coins, amt))
# edit distance in four steps: state (i, j) ┬╖ recurrence min(replace, insert, delete) ┬╖ base i or j = 0 ┬╖ bottom-up
def edit(a, b):
    T = [[0] * (len(b) + 1) for _ in range(len(a) + 1)]
    for i in range(len(a) + 1):
        for j in range(len(b) + 1):
            T[i][j] = j if i == 0 else i if j == 0 else min(T[i-1][j-1] + (a[i-1] != b[j-1]), T[i-1][j] + 1, T[i][j-1] + 1)
    return T[-1][-1]
print("edit('kitten', 'sitting') =", edit("kitten", "sitting"))
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

stairs(10) = 89, coin_change([1,5,10,25], 63) = 6, [4,7] рдиреЗ 5 рдмрдирд╡рдгреЗ -1, meetings ['chess', 'coding', 'art']. US-рдкреНрд░рдХрд╛рд░рдЪреНрдпрд╛ рдирд╛рдгреНрдпрд╛рдВрд╕рд╛рдареА greedy DP рд╢реА рд╕рд╣рдордд рд╣реЛрддреЛ, [1,3,4] рд╕рд╛рдареА DP 2 рджреЗрддреЗ рддрд┐рдереЗ greedy 3 рджреЗрддреЛ, рдЖрдгрд┐ edit distance 3 рдЖрд╣реЗ.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рдлрд│реНрдпрд╛рдЪреНрдпрд╛ рдорджрддреАрдиреЗ рддреБрдореНрд╣реА exponential recursion linear рдХреЗрд▓реЗ, table bottom-up рднрд░рд▓реЗ, рдЖрдгрд┐ рддреАрди рдирд╛рдгреНрдпрд╛рдВрдЪреНрдпрд╛ рдЙрджрд╛рд╣рд░рдгрд╛рдиреЗ greedy рд▓рд╛ рдЪреБрдХрддрд╛рдирд╛ рдкрдХрдбрд▓реЗ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: diff tools (edit distance), spell checkers, resource allocation, text wrapping, рдЖрдгрд┐ рдкрд░реНрдпрд╛рдпрд╛рдВрд╡рд░рдЪрд╛ рдкреНрд░рддреНрдпреЗрдХ "рд╕рд░реНрд╡рд╛рдд рдХрдореА / рд╕рд░реНрд╡рд╛рдд рдЬрд╛рд╕реНрдд / рд╕рд░реНрд╡рд╛рдд рд╕реНрд╡рд╕реНрдд" рдкреНрд░рд╢реНрди тАФ DP рдХрд┐рдВрд╡рд╛ рд╕рд┐рджреНрдз рдХреЗрд▓реЗрд▓рд╛ greedy, рдЕрдВрджрд╛рдЬ рдХрдзреАрдЪ рдирд╛рд╣реА.

тПня╕П рдкреБрдвреЗ

рдзрдбрд╛ 12 тАФ рдпреЛрдЧреНрдп structure рдирд┐рд╡рдбрдгреЗ: рдкреНрд░рд╢реНрди рдард░рд╡рддреЛ, рдЖрдгрд┐ рдХрд│рд╕-рдкреНрд░рдХрд▓реНрдк (capstone).

ЁЯза Lesson 11 тАФ Dynamic programming & greedy: answers on the board

ЁЯУН You are here: Lesson 11 of 12 ┬╖ Next: lesson-12-choosing


ЁЯУж What's in this branch

Lessons 01тАУ11. Turning an exponential recursion into a linear table, the four-step recipe, and the shortcut that works only when a proof says so.

ЁЯзТ Explain like I'm 5

Remember the head asking fib(25) 242,785 times? Put a board in the corridor: the first time anyone answers "fib(17) = 1597", write it up. The next person to be asked looks at the board instead. Now every question is answered once: 49 in all. That is memoisation. Or fill the board yourself from the bottom тАФ fib(0), fib(1), fib(2)тАж тАФ never asking anyone: that is tabulation.

Greedy is different: no board, just take the best-looking step every time. To fit the most club meetings into one room, always take the meeting that ends first. It works тАФ there is a proof. For coins of 1, 3 and 4 making 6, "biggest coin first" gives 4+1+1 = three coins; the board says 3+3 = two. Greedy is only right when someone proved it.

ЁЯЧ║я╕П Diagram

flowchart LR
  S0["stairs(0)=1"] --> S1["stairs(1)=1"] --> S2["stairs(2)=2"] --> S3["stairs(3)=3"] --> S4["stairs(4)=5"] --> Sn["тАж stairs(n) = stairs(nтИТ1) + stairs(nтИТ2)"]

тЭУ What

ЁЯдФ Why

DP is the difference between "runs in seconds" and "would not finish before the heat death of the universe" for a large family of optimisation problems; greedy is the difference between a clever O(n log n) and a wrong answer. Knowing which is which is the skill.

ЁЯФз How (in this repo)

fib_memo adds a dict to the recursion; stairs rolls two variables; coin_change fills a table of best counts and returns тИТ1 when impossible; max_meetings sorts by end time and takes what fits.

ЁЯзк Try it

python3 dsa/demo.py dp
python3 - <<'EOF'
import sys; sys.path.insert(0, "dsa"); from algorithms import coin_change
def greedy(coins, amount):
    n = 0
    for c in sorted(coins, reverse=True): n += amount // c; amount %= c
    return n if amount == 0 else -1
for coins, amt in (([1, 5, 10, 25], 63), ([1, 3, 4], 6), ([4, 7], 5)):
    print(coins, amt, "greedy:", greedy(coins, amt), "DP:", coin_change(coins, amt))
# edit distance in four steps: state (i, j) ┬╖ recurrence min(replace, insert, delete) ┬╖ base i or j = 0 ┬╖ bottom-up
def edit(a, b):
    T = [[0] * (len(b) + 1) for _ in range(len(a) + 1)]
    for i in range(len(a) + 1):
        for j in range(len(b) + 1):
            T[i][j] = j if i == 0 else i if j == 0 else min(T[i-1][j-1] + (a[i-1] != b[j-1]), T[i-1][j] + 1, T[i][j-1] + 1)
    return T[-1][-1]
print("edit('kitten', 'sitting') =", edit("kitten", "sitting"))
EOF

тЬЕ Verify тАФ what you should see

stairs(10) = 89, coin_change([1,5,10,25], 63) = 6, [4,7] making 5 is -1, meetings ['chess', 'coding', 'art']. Greedy agrees with DP for the US-style coins, gives 3 where DP gives 2 for [1,3,4], and edit distance is 3.

ЁЯПБ What you just proved

You turned an exponential recursion into a linear one with a board, filled a table bottom-up, and caught greedy being wrong with a three-coin example.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: diff tools (edit distance), spell checkers, resource allocation, text wrapping, and every "fewest / most / cheapest" question over choices тАФ DP or a proven greedy, never a guess.

тПня╕П Next

Lesson 12 тАФ choosing the right structure: the question decides, and the capstone.

тЖР PreviousgraphsNext тЖТchoosing

This page is the lesson's README from the lesson-11-dp-greedy branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.