Unbounded Knapsack and Coin Change II
Allow items to be reused by iterating capacity forwards, and solve coin-change-II (count ways) and rod-cutting using this variant.
Unbounded Knapsack and Coin Change II is a free DSA Interview Prep lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the DSA Interview Prep learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Unbounded Knapsack Concept
In Unbounded Knapsack, each item can be taken any number of times (unlike 0/1 knapsack where each item is used at most once). The state definition is the same — dp[c] = maximum value achievable with capacity c — but the iteration direction changes. Because items are reusable, when we update dp[c] we want to allow the current item to be used again, so we iterate capacity left to right (forward).
Forward Iteration Enables Reuse
Recall that in 0/1 knapsack we iterated right to left to prevent reuse. In unbounded knapsack we do the opposite: iterate left to right. When computing dp[c], dp[c-w] has already been updated in the current pass — meaning item i was possibly already included. This is exactly what we want: item i can be added again to a solution that already contains item 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: Count Ways
Coin Change II asks: given coin denominations and an amount, count the number of distinct ways to make that amount (each coin can be used unlimited times). This is an unbounded knapsack variant where instead of maximising value, we count combinations. Define dp[c] as the number of ways to make amount c. Base case: dp[0] = 1 (one way to make 0: take nothing).
Coin Change II Implementation
For each coin, iterate amounts left to right and accumulate: dp[c] += dp[c - coin]. The base case dp[0] = 1 seeds the count. Note the outer loop is over coins and the inner loop is over amounts — this naturally gives combination counts (not permutations), because each coin denomination is considered exactly once as an outer pass.
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 vs Permutations
The order of the loops matters critically. If we put amount in the outer loop and coin in the inner loop, we count permutations (order matters). For amount=5 with coins [1,2]: 1+2+2 and 2+1+2 are counted separately. If we put coin in the outer loop, we count combinations (order does not matter): 1+2+2 and 2+1+2 are the same. Coin Change II asks for combinations, so coin is the outer loop.
# 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])) # 13Rod Cutting Problem
Another classic unbounded knapsack problem: given a rod of length n and prices for each rod length 1 to n, find the maximum revenue by cutting the rod optimally. Each piece of length l can be sold for price[l], and pieces can be reused (the rod can be cut into multiple pieces of the same length). This maps directly to unbounded knapsack with W = n and items being the different cut lengths.
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: Minimum Coins
Coin Change I (a different problem) asks for the minimum number of coins to make a target amount. Here dp[c] = minimum coins to make amount c. Recurrence: dp[c] = min(dp[c], dp[c - coin] + 1). Initialise all entries to inf except dp[0] = 0. This is also unbounded (coins can be reused), so iterate left to right. Return dp[amount] if finite, else -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)) # -1Key Difference: Max vs Min vs Count
The three unbounded knapsack variants use different operations on dp[c-coin]: Maximise value: dp[c] = max(dp[c], dp[c-w] + v); initialise to 0. Minimise cost: dp[c] = min(dp[c], dp[c-coin] + 1); initialise to inf, dp[0]=0. Count ways: dp[c] += dp[c-coin]; initialise to 0, dp[0]=1. Recognising which variant applies is half the battle in interview problems.
Complexity and Interview Tips
All unbounded knapsack variants run in O(n × W) time and O(W) space where n is the number of item types and W is the target amount. For coin problems, n is the number of coin denominations. In interviews, state the variant (max/min/count), write the 1D DP, and be explicit about whether the outer loop is coins or amount — examiners know this distinction tests deep DP understanding.
Identifying Unbounded vs 0/1
Use these signals to identify which variant applies: unlimited reuse → unbounded (forward iteration); each item exactly once → 0/1 (backward iteration); problem says 'any number of times', 'infinite supply', or 'reuse allowed' → unbounded. Examples: coin change, rod cutting, integer break — all unbounded. Subset sum, partition, 0/1 knapsack — 0/1. Getting this wrong causes wrong answers that are hard to debug.
Integer Break and Other Variants
Integer Break (LeetCode 343): split an integer n into at least 2 positive integers to maximise their product. This is an unbounded knapsack where 'items' are the integers 2 through n-1. Define dp[i] = max product of integers summing to i. For each item j from 2 to i, dp[i] = max(dp[i], max(j, dp[j]) * max(i-j, dp[i-j])). This shows how the unbounded pattern generalises beyond the coin context.
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)Quick Check
Test your understanding of Data Structures & Algorithms — Coding Interview Prep concepts from this lesson.
Lesson Recap
In this lesson you learned: unbounded knapsack iterates capacity left to right to allow item reuse, Coin Change II counts combinations by putting coin in the outer loop, and the three variants — maximise, minimise, count — differ only in the DP operation and initialisation. Next up we use the 0/1 knapsack to solve Partition Equal Subset Sum.
Frequently asked questions
Is the “Unbounded Knapsack and Coin Change II” lesson free?
Yes — the full text of “Unbounded Knapsack and Coin Change II” is free to read here on the web, and the DSA Interview Prep course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the DSA Interview Prep course, upgrade to CoddyKit PRO.
What will I learn in “Unbounded Knapsack and Coin Change II”?
Allow items to be reused by iterating capacity forwards, and solve coin-change-II (count ways) and rod-cutting using this variant. You practise DSA Interview Prep with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start DSA Interview Prep?
No prior experience is required. DSA Interview Prep on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Unbounded Knapsack and Coin Change II” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this DSA Interview Prep lesson?
Yes. Every DSA Interview Prep lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- 0/1 Knapsack and Space Optimisation
- Unbounded Knapsack and Coin Change II
- Partition Equal Subset Sum
- Target Sum with Positive and Negative Signs