← Back to All Patterns

Unbounded Knapsack (Dynamic Programming)

Unbounded Knapsack Animation

Items (can use unlimited times):

Item 0: w=1, v=1
Item 1: w=3, v=4
Item 2: w=4, v=5
Capacity: 5

DP Array (Max value for each capacity):

0
cap 0
0
cap 1
0
cap 2
0
cap 3
0
cap 4
0
cap 5

Start: DP array initialized to 0. Items can be used unlimited times!

Speed:
Step 1 of 12

🎯 Explain Like I'm 5...

Imagine you're at a candy store 🍬 with $5! Unlike before, NOW you can buy the SAME candy MULTIPLE times! There are UNLIMITED of each candy!

🍭 The Candy Shop (UNLIMITED!):

  • Small candy: $1, yumminess=2 (as many as you want! 🍬🍬🍬)
  • Big candy: $3, yumminess=4 (unlimited! 🍭🍭🍭)

💰 Your $5 Budget - What's best?

  • Buy 5 small candies: $5, yumminess = 10 ✅
  • Buy 1 big + 2 small: $5, yumminess = 8 ❌ (not as good!)
  • The winner: 5 small candies!

Difference from 0/1 Knapsack: NOW you can pick the same item MANY times! 🔄

🚀 The Trick: Try using each candy MULTIPLE TIMES at each step! It's like having a magic candy machine that never runs out! 🎰

When Should You Use This Pattern?

  • Items can be used unlimited times
  • Coin change problems
  • minimum/maximum ways to make a target
  • Problems with infinite supply

📝 Example 1: Unbounded Knapsack

Given unlimited quantities of items with weights and values, maximize value for given capacity.

Step-by-Step Thinking:

  1. dp[w] = maximum value achievable with capacity w
  2. For each capacity, try adding each item type
  3. Can use same item multiple times!
  4. Traverse forward (unlike 0/1 knapsack)
UnboundedKnapsack.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class UnboundedKnapsack {
public static int unboundedKnapsack(int[] weights, int[] values, int capacity) {
int n = weights.length;
int[] dp = new int[capacity + 1];
// Build up from smaller capacities
for (int w = 1; w <= capacity; w++) {
// Try each item type
for (int i = 0; i < n; i++) {
// If item fits, try adding it
if (weights[i] <= w) {
dp[w] = Math.max(dp[w], values[i] + dp[w - weights[i]]);
}
}
}
return dp[capacity];
}
// Alternative: 2D DP for clarity
public static int unboundedKnapsack2D(int[] weights, int[] values, int capacity) {
int n = weights.length;
int[][] dp = new int[n + 1][capacity + 1];
for (int i = 1; i <= n; i++) {
for (int w = 1; w <= capacity; w++) {
// Option 1: Don't include this item type
dp[i][w] = dp[i - 1][w];
// Option 2: Include this item (can use again!)
if (weights[i - 1] <= w) {
// Note: dp[i][...] not dp[i-1][...] - can reuse!
dp[i][w] = Math.max(dp[i][w],
values[i - 1] + dp[i][w - weights[i - 1]]);
}
}
}
return dp[n][capacity];
}
public static void main(String[] args) {
int[] weights = {1, 3, 4, 5};
int[] values = {10, 40, 50, 70};
int capacity = 8;
System.out.println("Max value (1D): " + unboundedKnapsack(weights, values, capacity));
System.out.println("Max value (2D): " + unboundedKnapsack2D(weights, values, capacity));
// Output: 110 (take item 3 twice: 50+50+10)
}
}

📝 Example 2: Coin Change (Minimum Coins)

Find the minimum number of coins needed to make a given amount.

CoinChange.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class CoinChange {
public static int coinChange(int[] coins, int amount) {
// dp[i] = minimum coins needed to make amount i
int[] dp = new int[amount + 1];
// Initialize with "impossible" value
for (int i = 1; i <= amount; i++) {
dp[i] = Integer.MAX_VALUE;
}
dp[0] = 0; // Base case: 0 coins for amount 0
// For each amount
for (int a = 1; a <= amount; a++) {
// Try each coin
for (int coin : coins) {
if (coin <= a && dp[a - coin] != Integer.MAX_VALUE) {
dp[a] = Math.min(dp[a], 1 + dp[a - coin]);
}
}
}
return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
}
// With backtracking to show which coins
public static void printCoinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
int[] parent = new int[amount + 1];
for (int i = 1; i <= amount; i++) {
dp[i] = Integer.MAX_VALUE;
parent[i] = -1;
}
dp[0] = 0;
for (int a = 1; a <= amount; a++) {
for (int coin : coins) {
if (coin <= a && dp[a - coin] != Integer.MAX_VALUE) {
if (1 + dp[a - coin] < dp[a]) {
dp[a] = 1 + dp[a - coin];
parent[a] = coin;
}
}
}
}
if (dp[amount] == Integer.MAX_VALUE) {
System.out.println("Cannot make amount " + amount);
return;
}
System.out.print("Coins used: ");
int curr = amount;
while (curr > 0) {
System.out.print(parent[curr] + " ");
curr -= parent[curr];
}
System.out.println();
}
public static void main(String[] args) {
int[] coins = {1, 2, 5};
int amount = 11;
System.out.println("Minimum coins: " + coinChange(coins, amount));
printCoinChange(coins, amount);
// Output: 3 coins (5 + 5 + 1)
}
}

📝 Example 3: Coin Change (Number of Ways)

Count the number of ways to make a given amount using given coins.

CoinChangeWays.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class CoinChangeWays {
public static int change(int amount, int[] coins) {
// dp[a] = number of ways to make amount a
int[] dp = new int[amount + 1];
dp[0] = 1; // One way to make 0: use no coins
// Process each coin type
for (int coin : coins) {
// Update all amounts that can use this coin
for (int a = coin; a <= amount; a++) {
dp[a] += dp[a - coin];
}
}
return dp[amount];
}
// Important: Process coins in outer loop to avoid duplicates!
// If we process amounts in outer loop, [1,2] and [2,1] counted separately
public static void main(String[] args) {
int amount = 5;
int[] coins = {1, 2, 5};
System.out.println("Ways to make " + amount + ": " + change(amount, coins));
// Output: 4 ways
// [5], [2,2,1], [2,1,1,1], [1,1,1,1,1]
amount = 3;
coins = new int[]{2};
System.out.println("Ways to make " + amount + ": " + change(amount, coins));
// Output: 0 (cannot make 3 with only 2s)
}
}

🔑 Key Points to Remember

  • 1️⃣Items can be used unlimited times (vs 0/1 knapsack)
  • 2️⃣Traverse forward in 1D DP (vs backward for 0/1)
  • 3️⃣For counting ways: process items in outer loop to avoid duplicates
  • 4️⃣Time Complexity: O(n × capacity), Space: O(capacity)

💪 Practice Problems

  • Rod Cutting Problem
  • Maximum Ribbon Cut
  • Coin Change 2
  • Minimum Cost for Tickets