Unbounded Knapsack och Coin Change II
Tillåt att objekt återanvänds genom att iterera kapaciteten framåt, och lös coin-change-II (räkna antal sätt) samt rod-cutting med denna variant.
Unbounded Knapsack och Coin Change II är en gratis lektion i Förberedelse inför kodningsintervjuer på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Förberedelse inför kodningsintervjuer, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Förberedelse inför kodningsintervjuer innehåller totalt 4 lektioner.
Det obegränsade ryggsäcksproblemet
I det obegränsade ryggsäcksproblemet kan varje objekt väljas ett godtyckligt antal gånger (till skillnad från 0/1-ryggsäcksproblemet, där varje objekt används högst en gång). Tillståndsdefinitionen är densamma — dp[c] = det maximala värdet som kan uppnås med kapaciteten c — men iterationsriktningen ändras. Eftersom objekten kan återanvändas vill vi tillåta att det aktuella objektet används igen när vi uppdaterar dp[c], så vi itererar över kapaciteten från vänster till höger (framåt).
Framåtriktad iteration möjliggör återanvändning
Kom ihåg att vi i 0/1-ryggsäcksproblemet itererade från höger till vänster för att förhindra återanvändning. I det obegränsade ryggsäcksproblemet gör vi tvärtom: vi itererar från vänster till höger. När vi beräknar dp[c] har dp[c-w] redan uppdaterats i den aktuella genomgången — vilket betyder att objekt i redan kan ha inkluderats. Det är precis vad vi vill: objekt i kan läggas till igen i en lösning som redan innehåller objekt i.
def unbounded_knapsack(weights, values, W):
dp = [0] * (W + 1)
for i in range(len(weights)):
w, v = weights[i], values[i]
for c in range(w, W + 1): # iterate LEFT TO RIGHT
dp[c] = max(dp[c], dp[c - w] + v)
return dp[W]
weights = [1, 3, 4, 5]
values = [1, 4, 5, 7]
print(unbounded_knapsack(weights, values, 7)) # 9Coin Change II: räkna antalet sätt
Coin Change II frågar: givet myntvalörer och ett belopp, hur många olika sätt finns det att skapa beloppet (varje mynt kan användas obegränsat många gånger)? Detta är en variant av obegränsad ryggsäck där vi i stället för att maximera ett värde räknar kombinationer. Definiera dp[c] som antalet sätt att skapa beloppet c. Grundfall: dp[0] = 1 (ett sätt att skapa 0: att inte ta något).
Implementering av Coin Change II
För varje mynt itererar ni över beloppen från vänster till höger och summerar: dp[c] += dp[c - coin]. Grundfallet dp[0] = 1 initierar räkningen. Observera att den yttre loopen går över mynten och den inre loopen över beloppen — detta ger naturligt kombinationsantal (inte permutationer), eftersom varje myntvalör behandlas exakt en gång i en yttre genomgång.
def change(amount, coins):
dp = [0] * (amount + 1)
dp[0] = 1 # one way to make amount 0
for coin in coins:
for c in range(coin, amount + 1):
dp[c] += dp[c - coin]
return dp[amount]
print(change(5, [1, 2, 5])) # 4
print(change(3, [2])) # 0
print(change(10, [10])) # 1Kombinationer jämfört med permutationer
Looparnas ordning är avgörande. Om vi placerar beloppet i den yttre loopen och myntet i den inre loopen räknar vi permutationer (ordningen spelar roll). För amount=5 med mynten [1,2] räknas 1+2+2 och 2+1+2 separat. Om vi placerar myntet i den yttre loopen räknar vi kombinationer (ordningen spelar ingen roll): 1+2+2 och 2+1+2 är samma. Coin Change II frågar efter kombinationer, så myntet ska ligga i den yttre loopen.
# Count COMBINATIONS (order does not matter) — coin outer loop
def combinations(amount, coins):
dp = [0] * (amount + 1)
dp[0] = 1
for coin in coins: # coin outer
for c in range(coin, amount + 1):
dp[c] += dp[c - coin]
return dp[amount]
# Count PERMUTATIONS (order matters) — amount outer loop
def permutations(amount, coins):
dp = [0] * (amount + 1)
dp[0] = 1
for c in range(1, amount + 1): # amount outer
for coin in coins:
if c >= coin:
dp[c] += dp[c - coin]
return dp[amount]
print(combinations(5, [1,2,5])) # 4
print(permutations(5, [1,2,5])) # 13Stavkapningsproblemet
Ett annat klassiskt problem med obegränsad ryggsäck: givet en stav med längden n och priser för varje stavlängd från 1 till n, hitta den maximala intäkten genom att kapa staven optimalt. Varje del med längden l kan säljas för price[l], och delarna kan återanvändas (staven kan kapas i flera delar med samma längd). Detta motsvarar direkt obegränsad ryggsäck med W = n, där objekten är de olika kaplängderna.
def rod_cutting(prices, n):
# prices[i] = price of rod of length i+1
dp = [0] * (n + 1)
for length in range(1, n + 1): # each cut length
price = prices[length - 1]
for c in range(length, n + 1):
dp[c] = max(dp[c], dp[c - length] + price)
return dp[n]
prices = [1, 5, 8, 9, 10, 17, 17, 20]
print(rod_cutting(prices, 8)) # 22Coin Change I: minimalt antal mynt
Coin Change I (ett annat problem) frågar efter det minsta antalet mynt som krävs för att skapa ett målbelopp. Här är dp[c] = det minsta antalet mynt för att skapa beloppet c. Rekurrens: dp[c] = min(dp[c], dp[c - coin] + 1). Initiera alla poster till inf utom dp[0] = 0. Detta är också en obegränsad variant (mynt kan återanvändas), så iterera från vänster till höger. Returnera dp[amount] om värdet är ändligt, annars -1.
def coinChange(coins, amount):
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for c in range(coin, amount + 1):
dp[c] = min(dp[c], dp[c - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
print(coinChange([1,5,6,9], 11)) # 2 (5+6 or other combos)
print(coinChange([2], 3)) # -1Viktig skillnad: max, min och antal
De tre varianterna av obegränsad ryggsäck använder olika operationer på dp[c-coin]: Maximera värdet: dp[c] = max(dp[c], dp[c-w] + v); initiera med 0. Minimera kostnaden: dp[c] = min(dp[c], dp[c-coin] + 1); initiera med inf, dp[0]=0. Räkna antalet sätt: dp[c] += dp[c-coin]; initiera med 0, dp[0]=1. Att känna igen vilken variant som gäller är halva utmaningen i intervjuproblem.
Komplexitet och intervjutips
Alla varianter av obegränsad ryggsäck körs på O(n × W) tid och kräver O(W) utrymme, där n är antalet objekttyper och W är målbeloppet. För myntproblem är n antalet myntvalörer. Under intervjuer bör ni ange varianten (maximera/minimera/räkna), skriva 1D-DP och vara tydliga med om den yttre loopen går över mynt eller belopp — intervjuare vet att denna skillnad testar en djup förståelse av DP.
Identifiera obegränsad ryggsäck jämfört med 0/1
Använd följande signaler för att avgöra vilken variant som gäller: obegränsad återanvändning → obegränsad variant (framåtriktad iteration); varje objekt exakt en gång → 0/1 (bakåtriktad iteration); om problemet säger ”valfritt antal gånger”, ”obegränsad tillgång” eller ”återanvändning tillåten” → obegränsad variant. Exempel: Coin Change, Rod Cutting och Integer Break — alla är obegränsade. Subset Sum, Partition och 0/1 Knapsack — 0/1. Om ni väljer fel blir svaret fel, och sådana fel kan vara svåra att felsöka.
Integer Break och andra varianter
Integer Break (LeetCode 343): dela upp ett heltal n i minst två positiva heltal för att maximera deras produkt. Detta är en variant av obegränsad ryggsäck där ”objekten” är heltalen 2 till n-1. Definiera dp[i] = den maximala produkten av heltal vars summa är i. För varje objekt j från 2 till i gäller dp[i] = max(dp[i], max(j, dp[j]) * max(i-j, dp[i-j])). Detta visar hur mönstret för obegränsad ryggsäck kan generaliseras bortom myntproblem.
def integerBreak(n):
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
for j in range(1, i):
dp[i] = max(dp[i], max(j, dp[j]) * max(i-j, dp[i-j]))
return dp[n]
print(integerBreak(10)) # 36 (3+3+4 = 3*3*4 = 36)Snabbkontroll
Testa er förståelse av koncepten från Data Structures & Algorithms — Coding Interview Prep som tas upp i den här lektionen.
Tillbakablick på lektionen
I den här lektionen har ni lärt er: obegränsad ryggsäck itererar över kapaciteten från vänster till höger för att tillåta återanvändning av objekt, Coin Change II räknar kombinationer genom att placera coin i den yttre loopen, och de tre varianterna — maximera, minimera och räkna — skiljer sig endast i DP-operationen och initieringen. Härnäst använder vi 0/1-ryggsäcksproblemet för att lösa Partition Equal Subset Sum.
Lär dig Förberedelse inför kodningsintervjuer med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 90
- Lektioner
- 360
Vanliga frågor
Är lektionen ”Unbounded Knapsack och Coin Change II” gratis?
Ja – hela texten till ”Unbounded Knapsack och Coin Change II” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Förberedelse inför kodningsintervjuer, kan Ni uppgradera till CoddyKit PRO. Kursen i Förberedelse inför kodningsintervjuer innehåller totalt 4 lektioner.
Vad lär jag mig i ”Unbounded Knapsack och Coin Change II”?
Tillåt att objekt återanvänds genom att iterera kapaciteten framåt, och lös coin-change-II (räkna antal sätt) samt rod-cutting med denna variant. Ni övar på Förberedelse inför kodningsintervjuer med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Förberedelse inför kodningsintervjuer?
Du behöver inga förkunskaper. Utbildningen i Förberedelse inför kodningsintervjuer på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.
Hur lång tid tar lektionen ”Unbounded Knapsack och Coin Change II”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Förberedelse inför kodningsintervjuer-lektionen?
Ja. Varje Förberedelse inför kodningsintervjuer-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- 0/1-knapsack och utrymmesoptimering
- Unbounded Knapsack och Coin Change II
- Partition Equal Subset Sum
- Target Sum med positiva och negativa tecken