Skip to content

Commit 40b8bc5

Browse files
committed
Feat:add 0/1knapsack and 0/1knapsacktabulation along with their tests
1 parent 36fd4b2 commit 40b8bc5

File tree

8 files changed

+231
-200
lines changed

8 files changed

+231
-200
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
/**
4+
* The {@code KnapsackZeroOne} provides Recursive solution for the 0/1 Knapsack
5+
* problem. Solves by exploring all combinations of items using recursion. No
6+
* memoization or dynamic programming optimizations are applied.
7+
*
8+
* Time Complexity: O(2^n) — explores all subsets.
9+
* Space Complexity: O(n) — due to recursive call stack.
10+
*
11+
* Problem Reference: https://en.wikipedia.org/wiki/Knapsack_problem
12+
*/
13+
public final class KnapsackZeroOne {
14+
15+
private KnapsackZeroOne() {
16+
// Prevent instantiation
17+
}
18+
19+
/**
20+
* Solves the 0/1 Knapsack problem using recursion.
21+
*
22+
* @param values the array containing values of the items
23+
* @param weights the array containing weights of the items
24+
* @param capacity the total capacity of the knapsack
25+
* @param n the number of items
26+
* @return the maximum total value achievable within the given weight limit
27+
* @throws IllegalArgumentException if input arrays are null, empty, or
28+
* lengths mismatch
29+
*/
30+
public static int compute(final int[] values, final int[] weights, final int capacity, final int n) {
31+
if (values == null || weights == null) {
32+
throw new IllegalArgumentException("Input arrays cannot be null.");
33+
}
34+
if (values.length != weights.length) {
35+
throw new IllegalArgumentException("Value and weight arrays must be of the same length.");
36+
}
37+
if (capacity < 0 || n < 0) {
38+
throw new IllegalArgumentException("Invalid input: arrays must be non-empty and capacity/n "
39+
+ "non-negative.");
40+
}
41+
if (n == 0 || capacity == 0 || values.length == 0) {
42+
return 0;
43+
}
44+
45+
if (weights[n - 1] <= capacity) {
46+
final int include = values[n - 1] + compute(values, weights, capacity - weights[n - 1], n - 1);
47+
final int exclude = compute(values, weights, capacity, n - 1);
48+
return Math.max(include, exclude);
49+
} else {
50+
return compute(values, weights, capacity, n - 1);
51+
}
52+
}
53+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
/**
4+
* Tabulation (Bottom-Up) Solution for 0-1 Knapsack Problem.
5+
* This method uses dynamic programming to build up a solution iteratively,
6+
* filling a 2-D array where each entry dp[i][w] represents the maximum value
7+
* achievable with the first i items and a knapsack capacity of w.
8+
*
9+
* The tabulation approach is efficient because it avoids redundant calculations
10+
* by solving all subproblems in advance and storing their results, ensuring
11+
* each subproblem is solved only once. This is a key technique in dynamic programming,
12+
* making it possible to solve problems that would otherwise be infeasible due to
13+
* exponential time complexity in naive recursive solutions.
14+
*
15+
* Time Complexity: O(n * W), where n is the number of items and W is the knapsack capacity.
16+
* Space Complexity: O(n * W) for the DP table.
17+
*
18+
* For more information, see:
19+
* https://en.wikipedia.org/wiki/Knapsack_problem#Dynamic_programming
20+
*/
21+
public final class KnapsackZeroOneTabulation {
22+
23+
private KnapsackZeroOneTabulation() {
24+
// Prevent instantiation
25+
}
26+
27+
/**
28+
* Solves the 0-1 Knapsack problem using the bottom-up tabulation technique.
29+
*
30+
* @param values the values of the items
31+
* @param weights the weights of the items
32+
* @param capacity the total capacity of the knapsack
33+
* @param itemCount the number of items
34+
* @return the maximum value that can be put in the knapsack
35+
* @throws IllegalArgumentException if input arrays are null, of different lengths,
36+
* or if capacity or itemCount is invalid
37+
*/
38+
public static int compute(final int[] values, final int[] weights, final int capacity, final int itemCount) {
39+
if (values == null || weights == null) {
40+
throw new IllegalArgumentException("Values and weights arrays must not be null.");
41+
}
42+
if (values.length != weights.length) {
43+
throw new IllegalArgumentException("Values and weights arrays must be non-null and of same length");
44+
}
45+
if (capacity < 0) {
46+
throw new IllegalArgumentException("Capacity must not be negative.");
47+
}
48+
if (itemCount < 0 || itemCount > values.length) {
49+
throw new IllegalArgumentException("Item count must be between 0 and the length of the values array.");
50+
}
51+
52+
final int[][] dp = new int[itemCount + 1][capacity + 1];
53+
54+
for (int i = 1; i <= itemCount; i++) {
55+
final int currentValue = values[i - 1];
56+
final int currentWeight = weights[i - 1];
57+
58+
for (int w = 1; w <= capacity; w++) {
59+
if (currentWeight <= w) {
60+
final int includeItem = currentValue + dp[i - 1][w - currentWeight];
61+
final int excludeItem = dp[i - 1][w];
62+
dp[i][w] = Math.max(includeItem, excludeItem);
63+
} else {
64+
dp[i][w] = dp[i - 1][w];
65+
}
66+
}
67+
}
68+
69+
return dp[itemCount][capacity];
70+
}
71+
}

src/main/java/com/thealgorithms/dynamicprogramming/ZeroOneKnapsack.java

Lines changed: 0 additions & 38 deletions
This file was deleted.

src/main/java/com/thealgorithms/dynamicprogramming/ZeroOneKnapsackTab.java

Lines changed: 0 additions & 49 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
public class KnapsackZeroOneTabulationTest {
8+
9+
@Test
10+
public void basicCheck() {
11+
int[] values = {60, 100, 120};
12+
int[] weights = {10, 20, 30};
13+
int capacity = 50;
14+
int itemCount = values.length;
15+
16+
int expected = 220; // Best choice: item 1 (100) and item 2 (120)
17+
int result = KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount);
18+
assertEquals(expected, result);
19+
}
20+
21+
@Test
22+
public void emptyKnapsack() {
23+
int[] values = {};
24+
int[] weights = {};
25+
int capacity = 50;
26+
int itemCount = 0;
27+
28+
assertEquals(0, KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount));
29+
}
30+
31+
@Test
32+
public void zeroCapacity() {
33+
int[] values = {60, 100, 120};
34+
int[] weights = {10, 20, 30};
35+
int capacity = 0;
36+
int itemCount = values.length;
37+
38+
assertEquals(0, KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount));
39+
}
40+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
class KnapsackZeroOneTest {
8+
9+
@Test
10+
void basicCheck() {
11+
int[] values = {60, 100, 120};
12+
int[] weights = {10, 20, 30};
13+
int capacity = 50;
14+
int expected = 220;
15+
16+
int result = KnapsackZeroOne.compute(values, weights, capacity, values.length);
17+
assertEquals(expected, result);
18+
}
19+
20+
@Test
21+
void zeroCapacity() {
22+
int[] values = {10, 20, 30};
23+
int[] weights = {1, 1, 1};
24+
int capacity = 0;
25+
26+
int result = KnapsackZeroOne.compute(values, weights, capacity, values.length);
27+
assertEquals(0, result);
28+
}
29+
30+
@Test
31+
void zeroItems() {
32+
int[] values = {};
33+
int[] weights = {};
34+
int capacity = 10;
35+
36+
int result = KnapsackZeroOne.compute(values, weights, capacity, 0);
37+
assertEquals(0, result);
38+
}
39+
40+
@Test
41+
void weightsExceedingCapacity() {
42+
int[] values = {10, 20};
43+
int[] weights = {100, 200};
44+
int capacity = 50;
45+
46+
int result = KnapsackZeroOne.compute(values, weights, capacity, values.length);
47+
assertEquals(0, result);
48+
}
49+
50+
@Test
51+
void throwsOnNullArrays() {
52+
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(null, new int[] {1}, 10, 1));
53+
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {1}, null, 10, 1));
54+
}
55+
56+
@Test
57+
void throwsOnMismatchedArrayLengths() {
58+
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {10, 20}, new int[] {5}, 15, 2));
59+
}
60+
61+
@Test
62+
void throwsOnNegativeInputs() {
63+
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {10}, new int[] {5}, -1, 1));
64+
65+
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {10}, new int[] {5}, 5, -1));
66+
}
67+
}

src/test/java/com/thealgorithms/dynamicprogramming/ZeroOneKnapsackTabTest.java

Lines changed: 0 additions & 58 deletions
This file was deleted.

0 commit comments

Comments
 (0)