Knapsack illimitato e Coin Change II
Consenta di riutilizzare gli elementi iterando sulla capacità in avanti, quindi risolva coin-change-II (conteggio dei modi) e rod-cutting usando questa variante.
Knapsack illimitato e Coin Change II è una lezione Coding Interview Prep gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Coding Interview Prep, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Coding Interview Prep include 4 lezioni in totale.
Concetto dello zaino illimitato
Nello zaino illimitato, ogni elemento può essere preso un numero qualsiasi di volte (a differenza dello zaino 0/1, in cui ogni elemento viene usato al massimo una volta). La definizione dello stato è la stessa: dp[c] = valore massimo ottenibile con capacità c; cambia però la direzione di iterazione. Poiché gli elementi sono riutilizzabili, quando aggiorniamo dp[c] vogliamo consentire un nuovo utilizzo dell'elemento corrente, quindi iteriamo sulla capacità da sinistra a destra (in avanti).
L'iterazione in avanti consente il riutilizzo
Si ricordi che nello zaino 0/1 si iterava da destra a sinistra per impedire il riutilizzo. Nello zaino illimitato si fa il contrario: si itera da sinistra a destra. Nel calcolo di dp[c], dp[c-w] è già stato aggiornato durante il passaggio corrente: ciò significa che l'elemento i potrebbe essere già stato incluso. È esattamente ciò che si desidera: l'elemento i può essere aggiunto di nuovo a una soluzione che contiene già l'elemento 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: conteggio dei modi
Coin Change II chiede: date le denominazioni delle monete e un importo, contare il numero di modi distinti per ottenere quell'importo (ogni moneta può essere usata un numero illimitato di volte). Si tratta di una variante dello zaino illimitato in cui, invece di massimizzare il valore, si contano le combinazioni. Definisca dp[c] come il numero di modi per ottenere l'importo c. Caso base: dp[0] = 1 (un modo per ottenere 0: non prendere nulla).
Implementazione di Coin Change II
Per ogni moneta, iteri sugli importi da sinistra a destra e accumuli: dp[c] += dp[c - coin]. Il caso base dp[0] = 1 inizializza il conteggio. Si noti che il ciclo esterno scorre le monete e quello interno gli importi: in questo modo si ottengono naturalmente i conteggi delle combinazioni (non delle permutazioni), perché ogni denominazione viene considerata esattamente una volta durante il passaggio esterno.
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])) # 1Combinazioni e permutazioni
L'ordine dei cicli è fondamentale. Se si mette amount nel ciclo esterno e coin in quello interno, si contano le permutazioni (l'ordine è importante). Per amount=5 con coins [1,2], 1+2+2 e 2+1+2 vengono contate separatamente. Se si mette coin nel ciclo esterno, si contano le combinazioni (l'ordine non è importante): 1+2+2 e 2+1+2 sono la stessa combinazione. Coin Change II richiede le combinazioni, quindi coin va messo nel ciclo esterno.
# 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])) # 13Problema del taglio della sbarra
Un altro classico problema dello zaino illimitato: data una sbarra di lunghezza n e i prezzi per ogni lunghezza da 1 a n, trovi il ricavo massimo tagliando la sbarra in modo ottimale. Ogni pezzo di lunghezza l può essere venduto a price[l] e i pezzi possono essere riutilizzati (la sbarra può essere divisa in più pezzi della stessa lunghezza). Questo problema corrisponde direttamente allo zaino illimitato con W = n, in cui gli elementi sono le diverse lunghezze dei tagli.
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: numero minimo di monete
Coin Change I (un problema diverso) chiede il numero minimo di monete necessario per ottenere un importo obiettivo. Qui dp[c] = numero minimo di monete per ottenere l'importo c. Ricorrenza: dp[c] = min(dp[c], dp[c - coin] + 1). Inizializzi tutte le celle a inf, tranne dp[0] = 0. Anche questo è un problema illimitato (le monete possono essere riutilizzate), quindi iteri da sinistra a destra. Restituisca dp[amount] se è finito; altrimenti restituisca -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)) # -1Differenza fondamentale: massimo, minimo e conteggio
Le tre varianti dello zaino illimitato usano operazioni diverse su dp[c-coin]: Massimizzare il valore: dp[c] = max(dp[c], dp[c-w] + v); inizializzare a 0. Minimizzare il costo: dp[c] = min(dp[c], dp[c-coin] + 1); inizializzare a inf, dp[0]=0. Contare i modi: dp[c] += dp[c-coin]; inizializzare a 0, dp[0]=1. Riconoscere quale variante si applica è metà del lavoro nei problemi da colloquio.
Complessità e suggerimenti per i colloqui
Tutte le varianti dello zaino illimitato richiedono O(n × W) di tempo e O(W) di spazio, dove n è il numero di tipi di elementi e W è l'importo obiettivo. Nei problemi sulle monete, n è il numero di denominazioni. Nei colloqui, indichi la variante (massimo/minimo/conteggio), scriva la DP 1D e specifichi chiaramente se il ciclo esterno scorre le monete o l'importo: gli esaminatori sanno che questa distinzione verifica una comprensione approfondita della DP.
Distinguere tra zaino illimitato e zaino 0/1
Usi questi segnali per identificare la variante corretta: riutilizzo illimitato → zaino illimitato (iterazione in avanti); ogni elemento esattamente una volta → zaino 0/1 (iterazione all'indietro); se il problema dice 'any number of times', 'infinite supply' o 'reuse allowed' → zaino illimitato. Esempi: cambio delle monete, taglio della sbarra e Integer Break sono tutti problemi illimitati. Subset sum, partition e 0/1 knapsack sono problemi 0/1. Sbagliare questa distinzione porta a risposte errate difficili da correggere.
Integer Break e altre varianti
Integer Break (LeetCode 343): dividere un intero n in almeno 2 interi positivi per massimizzarne il prodotto. Si tratta di uno zaino illimitato in cui gli 'elementi' sono gli interi da 2 a n-1. Definisca dp[i] = prodotto massimo di interi la cui somma è i. Per ogni elemento j da 2 a i, dp[i] = max(dp[i], max(j, dp[j]) * max(i-j, dp[i-j])). Questo mostra come il modello dello zaino illimitato si generalizzi oltre il contesto delle monete.
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)Verifica rapida
Metta alla prova la sua comprensione dei concetti di Data Structures & Algorithms — Coding Interview Prep trattati in questa lezione.
Riepilogo della lezione
In questa lezione ha imparato: lo zaino illimitato itera sulla capacità da sinistra a destra per consentire il riutilizzo degli elementi, Coin Change II conta le combinazioni mettendo coin nel ciclo esterno e le tre varianti — massimizzare, minimizzare e contare — differiscono solo per l'operazione DP e l'inizializzazione. Ora useremo lo zaino 0/1 per risolvere Partition Equal Subset Sum.
Domande Frequenti
La lezione «Knapsack illimitato e Coin Change II» è gratuita?
Sì — il testo completo di «Knapsack illimitato e Coin Change II» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Coding Interview Prep, passa a CoddyKit PRO. Il corso Coding Interview Prep include 4 lezioni in totale.
Cosa imparerò in «Knapsack illimitato e Coin Change II»?
Consenta di riutilizzare gli elementi iterando sulla capacità in avanti, quindi risolva coin-change-II (conteggio dei modi) e rod-cutting usando questa variante. Eserciti Coding Interview Prep con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Coding Interview Prep?
Non è richiesta alcuna esperienza precedente. Coding Interview Prep su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «Knapsack illimitato e Coin Change II»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Coding Interview Prep?
Sì. Ogni lezione Coding Interview Prep include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Knapsack 0/1 e ottimizzazione dello spazio
- Knapsack illimitato e Coin Change II
- Somma di sottoinsiemi con partizione equa
- Somma obiettivo con segni positivi e negativi