Sac à dos illimité et rendu de monnaie II
Autorisez la réutilisation des objets en parcourant la capacité dans le sens croissant, puis résolvez coin-change-II (compter les façons) et le découpage de tiges avec cette variante.
Sac à dos illimité et rendu de monnaie II est une leçon Coding Interview Prep gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Coding Interview Prep, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Coding Interview Prep comprend 4 leçons au total.
Concept du sac à dos non borné
Dans le sac à dos non borné, chaque élément peut être pris un nombre quelconque de fois (contrairement au sac à dos 0/1, où chaque élément est utilisé au plus une fois). La définition de l’état reste la même — dp[c] = valeur maximale atteignable avec une capacité c —, mais le sens du parcours change. Comme les éléments sont réutilisables, lorsque nous mettons à jour dp[c], nous voulons permettre une nouvelle utilisation de l’élément courant ; nous parcourons donc la capacité de gauche à droite (vers l’avant).
Le parcours vers l’avant permet la réutilisation
Rappelez-vous que, dans le sac à dos 0/1, nous parcourions la capacité de droite à gauche pour empêcher la réutilisation. Dans le sac à dos non borné, nous faisons l’inverse : nous parcourons la capacité de gauche à droite. Lors du calcul de dp[c], dp[c-w] a déjà été mis à jour au cours du passage actuel ; cela signifie que l’élément i a peut-être déjà été inclus. C’est exactement ce que nous voulons : l’élément i peut être ajouté une nouvelle fois à une solution qui le contient déjà.
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)) # 9Rendu de monnaie II : compter les façons
Le rendu de monnaie II demande, étant donné des valeurs de pièces et un montant, de compter le nombre de façons distinctes d’obtenir ce montant (chaque pièce peut être utilisée sans limite). Il s’agit d’une variante du sac à dos non borné dans laquelle, au lieu de maximiser une valeur, nous comptons les combinations. Définissez dp[c] comme le nombre de façons d’obtenir le montant c. Cas de base : dp[0] = 1 (une façon d’obtenir 0 : ne rien prendre).
Implémentation du rendu de monnaie II
Pour chaque pièce, parcourez les montants de gauche à droite et cumulez : dp[c] += dp[c - coin]. Le cas de base dp[0] = 1 initialise le décompte. Notez que la boucle externe porte sur les pièces et la boucle interne sur les montants : cela produit naturellement un décompte des combinations (et non des permutations), car chaque valeur de pièce est prise en compte exactement une fois lors d’un passage externe.
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])) # 1Combinations ou permutations
L’ordre des boucles est déterminant. Si nous plaçons le montant dans la boucle externe et la pièce dans la boucle interne, nous comptons des permutations (l’ordre compte). Pour amount=5 avec les pièces [1,2], 1+2+2 et 2+1+2 sont comptés séparément. Si nous plaçons la pièce dans la boucle externe, nous comptons des combinations (l’ordre ne compte pas) : 1+2+2 et 2+1+2 sont identiques. Le rendu de monnaie II demande des combinations ; la pièce doit donc être dans la boucle externe.
# 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])) # 13Problème de découpe d’une barre
Autre problème classique du sac à dos non borné : étant donné une barre de longueur n et les prix correspondant à chaque longueur de barre de 1 à n, trouvez la recette maximale en découpant la barre de manière optimale. Chaque morceau de longueur l peut être vendu au prix price[l], et les morceaux peuvent être réutilisés (la barre peut être découpée en plusieurs morceaux de même longueur). Ce problème correspond directement au sac à dos non borné avec W = n, les éléments étant les différentes longueurs de découpe.
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)) # 22Rendu de monnaie I : nombre minimal de pièces
Le rendu de monnaie I (un problème différent) demande le nombre minimal de pièces nécessaires pour obtenir un montant cible. Ici, dp[c] = nombre minimal de pièces pour obtenir le montant c. Récurrence : dp[c] = min(dp[c], dp[c - coin] + 1). Initialisez toutes les entrées à inf, sauf dp[0] = 0. Ce problème est également non borné (les pièces peuvent être réutilisées), donc parcourez les montants de gauche à droite. Renvoyez dp[amount] si sa valeur est finie, sinon -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)) # -1Différence essentielle : maximum, minimum ou décompte
Les trois variantes du sac à dos non borné utilisent des opérations différentes sur dp[c-coin] : Maximiser la valeur : dp[c] = max(dp[c], dp[c-w] + v) ; initialisation à 0. Minimiser le coût : dp[c] = min(dp[c], dp[c-coin] + 1) ; initialisation à inf, dp[0]=0. Compter les façons : dp[c] += dp[c-coin] ; initialisation à 0, dp[0]=1. Reconnaître la variante applicable représente déjà la moitié du travail dans les problèmes d’entretien.
Complexité et conseils pour les entretiens
Toutes les variantes du sac à dos non borné s’exécutent en O(n × W) en temps et O(W) d’espace, où n est le nombre de types d’éléments et W le montant cible. Pour les problèmes de rendu de monnaie, n correspond au nombre de valeurs de pièces. En entretien, indiquez la variante (maximum, minimum ou décompte), écrivez la DP en 1D et précisez si la boucle externe porte sur les pièces ou sur le montant : les examinateurs savent que cette distinction permet d’évaluer une compréhension approfondie de la DP.
Distinguer le sac à dos non borné du sac à dos 0/1
Utilisez les indications suivantes pour identifier la variante applicable : réutilisation illimitée → sac à dos non borné (parcours vers l’avant) ; chaque élément exactement une fois → sac à dos 0/1 (parcours en sens inverse) ; l’énoncé indique « un nombre quelconque de fois », « une quantité infinie » ou « la réutilisation est autorisée » → sac à dos non borné. Exemples : rendu de monnaie, découpe d’une barre, décomposition d’un entier — tous non bornés. Somme de sous-ensembles, partition, sac à dos 0/1 — problèmes 0/1. Se tromper sur ce point entraîne des réponses incorrectes difficiles à déboguer.
Décomposition d’un entier et autres variantes
Décomposition d’un entier (LeetCode 343) : diviser un entier n en au moins 2 entiers positifs afin de maximiser leur produit. Il s’agit d’un sac à dos non borné dans lequel les « éléments » sont les entiers de 2 à n-1. Définissez dp[i] comme le produit maximal d’entiers dont la somme vaut i. Pour chaque élément j de 2 à i, dp[i] = max(dp[i], max(j, dp[j]) * max(i-j, dp[i-j])). Cela montre comment le schéma du sac à dos non borné se généralise au-delà du contexte du rendu de monnaie.
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)Vérification rapide
Évaluez votre compréhension des notions de structures de données et d’algorithmes — préparation aux entretiens de programmation — abordées dans cette leçon.
Récapitulatif de la leçon
Dans cette leçon, vous avez appris : le sac à dos non borné parcourt la capacité de gauche à droite afin de permettre la réutilisation des éléments, le rendu de monnaie II compte les combinations en plaçant la pièce dans la boucle externe, et les trois variantes — maximiser, minimiser, compter — ne diffèrent que par l’opération appliquée à la DP et son initialisation. Ensuite, nous utiliserons le sac à dos 0/1 pour résoudre le problème de partition en sous-ensembles de somme égale.
Questions Fréquemment Posées
La leçon « Sac à dos illimité et rendu de monnaie II » est-elle gratuite ?
Oui — le texte complet de « Sac à dos illimité et rendu de monnaie II » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Coding Interview Prep, passe à CoddyKit PRO. Le cours Coding Interview Prep comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Sac à dos illimité et rendu de monnaie II » ?
Autorisez la réutilisation des objets en parcourant la capacité dans le sens croissant, puis résolvez coin-change-II (compter les façons) et le découpage de tiges avec cette variante. Tu pratiques Coding Interview Prep avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Coding Interview Prep ?
Aucune expérience préalable n'est requise. Coding Interview Prep sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.
Combien de temps prend la leçon « Sac à dos illimité et rendu de monnaie II » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Coding Interview Prep ?
Oui. Chaque leçon Coding Interview Prep inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Sac à dos 0/1 et optimisation de l’espace
- Sac à dos illimité et rendu de monnaie II
- Partition en sous-ensembles de somme égale
- Somme cible avec signes positifs et négatifs