Coin Change og trapp med minimale kostnader
Formuler rekurrensene for coin-change og min-cost-climbing-stairs, velg riktig DP-retning og følg tabellen manuelt.
Coin Change og trapp med minimale kostnader er en gratis leksjon i Forberedelse til kodeintervjuer på CoddyKit. Dette er leksjon 4 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Forberedelse til kodeintervjuer, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Forberedelse til kodeintervjuer inneholder totalt 4 leksjoner.
Myntveksling: Problemet
Myntveksling (LeetCode #322) gir Dem myntvalører og et målbeløp. Finn minimumsantallet mynter som trengs for å oppnå nøyaktig dette beløpet. De har ubegrenset tilgang på mynter av hver valør. Dette er en klassisk variant av ubegrenset ryggsekk — hvert element (hver mynt) kan brukes et hvilket som helst antall ganger. Det er et av de viktigste DP-problemene fordi det tester evnen Deres til å formulere en rekurrens fra grunnen av.
# Problem examples:
# coins=[1,5,6,9], amount=11 -> 2 (5+6 or 2+9? no: 5+6=11 YES)
# coins=[2], amount=3 -> -1 (impossible)
# coins=[1,2,5], amount=11 -> 3 (5+5+1)
# coins=[186,419,83,408], amount=6249 -> 20
# Key choices:
# - Try each coin denomination at each step
# - Minimum coins = 1 + minimum(coins to make amount - coin)
# - If amount < 0: impossible
# - If amount = 0: done (0 coins)
print('Coin change: unbounded knapsack, find minimum count')Myntveksling: Utledning av rekurrensen
Definer dp[i] = minimumsantallet mynter for å oppnå beløpet i. For hvert beløp i prøver De å bruke hver mynt c: hvis i >= c, er dp[i] = min(dp[i], 1 + dp[i-c]). Tallet «1» står for mynten vi nettopp brukte; dp[i-c] er den optimale løsningen for det gjenværende beløpet. Dette forutsetter et ubegrenset antall mynter. Basistilfellet er dp[0] = 0. Initialiser alle andre oppføringer med uendelig for å representere «ikke oppnåelig ennå».
def coin_change(coins, amount):
# dp[i] = min coins to make amount i
dp = [float('inf')] * (amount + 1)
dp[0] = 0 # base: 0 coins for amount 0
for i in range(1, amount + 1):
for coin in coins:
if i >= coin and dp[i - coin] != float('inf'):
dp[i] = min(dp[i], 1 + dp[i - coin])
return dp[amount] if dp[amount] != float('inf') else -1
print(coin_change([1, 5, 6, 9], 11)) # 2
print(coin_change([2], 3)) # -1
print(coin_change([1, 2, 5], 11)) # 3
# Trace dp for coins=[1,5] amount=6:
# dp[0]=0, dp[1]=1, dp[2]=2, dp[3]=3, dp[4]=4, dp[5]=1, dp[6]=2Myntveksling: Hvorfor grådig algoritme ikke fungerer
En grådig algoritme (velg alltid den største mynten som passer) fungerer ikke for myntveksling. Eksempel: coins=[1, 3, 4], amount=6. Den grådige algoritmen velger 4 og deretter 1+1, altså 3 mynter. Det optimale valget er 3+3, altså 2 mynter. En grådig algoritme fungerer for standardvalører (1, 5, 10 og 25 cent) fordi disse tilfeldigvis oppfyller den grådige egenskapen. For vilkårlige myntsett kreves imidlertid DP. Dette er et klassisk poeng i jobbintervjuer — å si at en grådig algoritme ikke fungerer, og forklare hvorfor, viser sterke analytiske ferdigheter.
# Greedy failure example:
# coins=[1,3,4], amount=6
# Greedy: 4 (rem=2), 1 (rem=1), 1 (rem=0) -> 3 coins
# Optimal: 3 (rem=3), 3 (rem=0) -> 2 coins
def coin_change_greedy_wrong(coins, amount):
coins_sorted = sorted(coins, reverse=True)
count = 0
for coin in coins_sorted:
while amount >= coin:
amount -= coin
count += 1
return count if amount == 0 else -1
print('Greedy:', coin_change_greedy_wrong([1,3,4], 6)) # 3 (WRONG)
print('DP: ', coin_change([1,3,4], 6)) # 2 (CORRECT)Myntveksling II: Tell antallet måter
Myntveksling II (LeetCode #518) spør etter antallet måter å oppnå beløpet på (ikke minimumsantallet). Rekurrensen endres: I stedet for min bruker vi sum. dp[i] += dp[i-coin] for hver mynt. Utfyllingsrekkefølgen er viktig: For å telle hver kombinasjon én gang itererer De over mynter i den ytre løkken og beløp i den indre løkken. Hvis løkkene byttes om, telles permutasjoner i stedet for kombinasjoner (et annet problem).
def coin_change_ii(coins, amount):
# dp[i] = number of ways to make amount i
dp = [0] * (amount + 1)
dp[0] = 1 # one way to make amount 0: use no coins
# Outer loop: coins -- ensures each coin type processed once
for coin in coins:
# Inner loop: amounts
for i in range(coin, amount + 1):
dp[i] += dp[i - coin]
return dp[amount]
print(coin_change_ii([1, 2, 5], 5)) # 4: [1,1,1,1,1],[1,1,1,2],[1,2,2],[5]
print(coin_change_ii([2], 3)) # 0: impossible
print(coin_change_ii([10], 10)) # 1
# Key: coin outer, amount inner = COMBINATIONS (unordered)
# Reverse (amount outer, coin inner) = PERMUTATIONS (ordered)Trappetrinn med minimumskostnad: Problemet
Trappetrinn med minimumskostnad (LeetCode #746) gir Dem en trapp der hvert trinn har en kostnad. De kan gå 1 eller 2 trinn om gangen. Finn minimumskostnaden for å nå toppen (ett trinn etter det siste trappetrinnet). De kan starte gratis på trinn 0 eller trinn 1. Dette problemet kombinerer på en elegant måte rekurrensen for trappegåing med mønsteret for kostnadsminimering i myntveksling, og fungerer derfor som en naturlig overgang mellom de to.
# cost = [10, 15, 20]
# Pay cost[i] to leave step i
# You can step to i+1 or i+2
# Goal: reach top (index 3) with minimum cost
# Path options:
# Start at 0: cost 10, go to 2: cost 20, done -> 30
# Start at 1: cost 15, go to 3: done -> 15 <- OPTIMAL
# Start at 0: cost 10, go to 1: cost 15 -> 25
cost = [10, 15, 20]
# Optimal: start at step 1, pay 15, jump to top -> cost = 15
print('Expected:', 15)Trappetrinn med minimumskostnad: Rekurrensen
Definer dp[i] = minimumskostnaden for å nå trinn i. De kommer til trinn i ved å betale cost[i-1] (fra trinn i-1) eller cost[i-2] (fra trinn i-2). Derfor er dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]). Basistilfeller: dp[0] = 0 (starten før trappen, gratis), dp[1] = 0 (De kan også starte på trinn 1, gratis). Svaret er dp[n], der n = len(cost).
def min_cost_climbing_stairs(cost):
n = len(cost)
# dp[i] = minimum cost to reach step i
# Steps 0 to n; step n is the top (goal)
dp = [0] * (n + 1)
# dp[0] = 0 (free to start here)
# dp[1] = 0 (free to start here)
for i in range(2, n + 1):
dp[i] = min(dp[i-1] + cost[i-1], # step from i-1
dp[i-2] + cost[i-2]) # jump from i-2
return dp[n]
print(min_cost_climbing_stairs([10, 15, 20])) # 15
print(min_cost_climbing_stairs([1,100,1,1,1,100,1,1,100,1])) # 6Trappetrinn med minimumskostnad: Minneoptimalisering
Siden dp[i] bare avhenger av dp[i-1] og dp[i-2], kan vi redusere minnebruken til O(1) med to variabler, akkurat som i Fibonacci. Erstatt arrayen med prev2 og prev1. Oppdater dem ved hvert trinn. Dette er en standardoptimalisering på én linje som intervjuere forventer etter at De har presentert tabelløsningen med O(n). Nevn det alltid på eget initiativ: «Vi kan redusere dette til O(1) minne siden vi bare trenger de to siste verdiene.»
def min_cost_optimised(cost):
n = len(cost)
prev2, prev1 = 0, 0 # dp[0] and dp[1]
for i in range(2, n + 1):
curr = min(prev1 + cost[i-1], prev2 + cost[i-2])
prev2, prev1 = prev1, curr
return prev1
print(min_cost_optimised([10, 15, 20])) # 15
print(min_cost_optimised([1,100,1,1,1,100,1,1,100,1])) # 6
# Alternative: directly use cost array as rolling storage
def min_cost_v2(cost):
n = len(cost)
for i in range(2, n):
cost[i] += min(cost[i-1], cost[i-2])
return min(cost[-1], cost[-2])
from copy import deepcopy
cost_test = [10,15,20]
print(min_cost_v2(deepcopy(cost_test))) # 15Alternativ DP-formulering
Noen problemer har flere gyldige DP-formuleringer. For trappetrinn med minimumskostnad kan De definere dp[i] = minimumskostnaden for å FORLATE trinn i (betal cost[i] og velg å gå til i+1 eller i+2). Da er dp[i] = cost[i] + min(dp[i+1], dp[i+2]) når tabellen fylles fra høyre mot venstre, og svaret er min(dp[0], dp[1]). Begge formuleringene er riktige. Øv på å forklare hvilken formulering De valgte, og hvorfor — det viser at De behersker DP.
def min_cost_alternative(cost):
n = len(cost)
# dp[i] = min cost when starting FROM step i
# Fill right to left
dp = cost[:] + [0] # dp[n] = 0 (already at top)
for i in range(n - 1, -1, -1):
# Pay cost[i], then choose i+1 or i+2
if i + 2 <= n:
dp[i] = cost[i] + min(dp[i+1], dp[i+2])
else:
dp[i] = cost[i] + dp[i+1]
# Can start at step 0 or step 1
return min(dp[0], dp[1])
print(min_cost_alternative([10, 15, 20])) # 15
print(min_cost_alternative([1,100,1,1,1,100,1,1,100,1])) # 6Sammenhengen mellom myntveksling og trappetrinn
Både myntveksling og trappetrinn med minimumskostnad er eksempler på det samme DP-mønsteret: Ved hvert trinn velger man fra et begrenset sett med alternativer og optimaliserer et mål over sekvensen av valg. Forskjellene er overfladiske: Myntveksling holder styr på antallet (legger til 1 per mynt), mens trappetrinn holder styr på kostnaden (legger til cost[i] per trinn). Når De kjenner igjen denne felles strukturen, kan De løse nye DP-problemer ved å knytte dem til kjente maler.
# Shared pattern:
# dp[state] = optimise(dp[prev_state_1] + cost_1,
# dp[prev_state_2] + cost_2, ...)
# Coin change: dp[amount] = min(1 + dp[amount - coin] for coin in coins)
# Min stair: dp[step] = min(cost[step-1]+dp[step-1], cost[step-2]+dp[step-2])
# Max path sum: dp[cell] = max(dp[top], dp[left]) + grid[cell]
# House robber: dp[house] = max(dp[house-1], dp[house-2] + value[house])
# All four are the SAME pattern with different:
# - State representation
# - Number of choices per state
# - Objective (min/max)
# - Transition cost
print('DP pattern: state + choices + objective + cost = template')Minimumsantall perfekte kvadrater
Perfekte kvadrater (LeetCode #279) spør etter minimumsantallet perfekte kvadrater (1, 4, 9, 16, ...) som summerer til n. Dette er nøyaktig myntveksling der «myntene» er perfekte kvadrater. Generer alle perfekte kvadrater opptil n, og kjør deretter myntveksling. DP gir O(n * sqrt(n))-tid. Lagranges firekvadratteorem sier at svaret høyst er 4, noe som også muliggjør en matematisk tilnærming med O(sqrt(n)) — men DP er den forventede løsningen.
import math
def num_squares(n):
# Generate all perfect squares up to n
squares = [i*i for i in range(1, int(math.sqrt(n)) + 1)]
# Coin change with squares as 'coins'
dp = [float('inf')] * (n + 1)
dp[0] = 0
for i in range(1, n + 1):
for sq in squares:
if i >= sq:
dp[i] = min(dp[i], 1 + dp[i - sq])
return dp[n]
print(num_squares(12)) # 3: 4+4+4
print(num_squares(13)) # 2: 4+9
print(num_squares(1)) # 1: 1Feilsøking av DP: Vanlige feil
Vanlige DP-feil: feil basistilfelle (dp[0] er satt feil), feil utfyllingsrekkefølge (man leser en verdi som ikke er beregnet ennå), feil med én i indeks eller tilstandens definisjon (dp[i] er kostnaden FOR å nå i, eller kostnaden for å FORLATE i), og at man ikke returnerer -1 når uendelig fortsatt gjenstår (umulige tilfeller). Test alltid de enkleste tilfellene først (tom inndata, ett element, target=0) før De tester større inndata.
# Common DP debugging checklist:
# 1. Base case: what is dp[0]? dp[1]? Are they correct?
# 2. State definition: write it in English before coding
# 3. Recurrence: trace manually on a 3-element example
# 4. Fill order: dependency arrows point left/up? Fill left/up first
# 5. Infinity check: return -1 or 0 when dp[target] == inf?
# 6. Array bounds: dp has size n+1 for 0..n, or n for 0..n-1?
# Quick test template:
def test_coin_change():
assert coin_change([1], 0) == 0 # base case
assert coin_change([1], 1) == 1 # single coin
assert coin_change([2], 3) == -1 # impossible
assert coin_change([1,5,6,9], 11) == 2
print('All tests passed!')
test_coin_change()Kunnskapssjekk
Test forståelsen Deres av begrepene fra Data Structures & Algorithms — Coding Interview Prep i denne leksjonen.
Oppsummering av leksjonen
I denne leksjonen har De lært: DP for minimumsantallet i myntveksling (ubegrenset ryggsekk) og hvorfor en grådig algoritme ikke fungerer, myntveksling II for å telle kombinasjoner med mynter i ytre løkke og beløp i indre løkke, samt trappetrinn med minimumskostnad med både venstre-til-høyre- og høyre-til-venstre-formuleringer. Deretter skal vi utforske 1D-DP-mønstre med husrøveren, Kadane's algorithm og orddeling.
Lær deg Forberedelse til kodeintervjuer med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 90
- Leksjoner
- 360
Ofte stilte spørsmål
Er leksjonen «Coin Change og trapp med minimale kostnader» gratis?
Ja – hele teksten i «Coin Change og trapp med minimale kostnader» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Forberedelse til kodeintervjuer-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Forberedelse til kodeintervjuer inneholder totalt 4 leksjoner.
Hva lærer jeg i «Coin Change og trapp med minimale kostnader»?
Formuler rekurrensene for coin-change og min-cost-climbing-stairs, velg riktig DP-retning og følg tabellen manuelt. Du øver på Forberedelse til kodeintervjuer med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med Forberedelse til kodeintervjuer?
Ingen tidligere erfaring er nødvendig. Forberedelse til kodeintervjuer på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.
Hvor lang tid tar leksjonen «Coin Change og trapp med minimale kostnader»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne Forberedelse til kodeintervjuer-leksjonen?
Ja. Alle Forberedelse til kodeintervjuer-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Gjenkjenne DP: overlappende delproblemer
- Top-down-DP med memoisation
- Bottom-up-DP med tabulering
- Coin Change og trapp med minimale kostnader