├── .gitignore ├── C ├── Fibonacci.c ├── MakingChange.c └── SquareSubmatrix.c ├── Fibonacci.java ├── Knapsack.java ├── MakingChange.java ├── README.md ├── SquareSubmatrix.java ├── TargetSum.java ├── cpp ├── Fibonacci.cpp ├── Knapsack.cpp ├── MakingChange.cpp ├── SquareSubmatrix.cpp └── TargetSum.cpp └── python ├── Fibonacci.py ├── Knapsack.py ├── MakingChange.py ├── SquareSubmatrix.py └── TargetSum.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *~ 3 | -------------------------------------------------------------------------------- /C/Fibonacci.c: -------------------------------------------------------------------------------- 1 | // 2 | // Fibonacci.c 3 | // DynamicProgrammingEbook 4 | // 5 | // Created by Arjun Singh on 07/29/19. 6 | // 7 | 8 | #include 9 | 10 | // Naive recursive Fibonacci solution 11 | int naive(int n){ 12 | if(n < 2) 13 | return n; 14 | return naive(n-1) + naive(n-2); 15 | } 16 | 17 | // Bottom-up dynamic Fibonacci solution 18 | int bottomUp(int n){ 19 | if(n<2) 20 | return n; 21 | int t1 = 1, t2 = 0, i; 22 | for(i = 2; i=0) 32 | return cache[n]; 33 | 34 | cache[n] = topDown(n-1, cache) + topDown(n-2, cache); 35 | return cache[n]; 36 | } 37 | 38 | // Top-down dynamic Fibonacci solution 39 | int topDown(int n){ 40 | if(n < 2) 41 | return n; 42 | int cache[n+1], i; 43 | for(i = 0; i<=n ; i++) 44 | cache[i] = -1; 45 | // Fill initial values in cache 46 | cache[0] = 0; 47 | cache[1] = 1; 48 | return topDown1(n, cache); 49 | } 50 | 51 | 52 | void test(){ 53 | int i; 54 | printf("Naive Method:\n"); 55 | for (i=-2; i<9; i++) { 56 | printf("i=%d, naive(i)=%d\n", i, naive(i)); 57 | } 58 | printf("\nTop-Down Method:\n"); 59 | for (i=-2; i<9; i++) { 60 | printf("i=%d, topDown(i)=%d\n", i, topDown(i)); 61 | } 62 | printf("\nBottom-Up Method:\n"); 63 | for (i=-2; i<9; i++) { 64 | printf("i=%d, bottomUp(i)=%d\n", i, bottomUp(i)); 65 | } 66 | } 67 | 68 | int main(){ 69 | test(); 70 | return 0; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /C/MakingChange.c: -------------------------------------------------------------------------------- 1 | // 2 | // MakingChange.cpp 3 | // DynamicProgrammingEbook 4 | // 5 | // Created by Arjun Singh on 07/29/19. 6 | // 7 | 8 | #include 9 | #include 10 | 11 | int bruteForce(int c, int coins[], int coins_len){ 12 | if(c == 0) 13 | return 0; 14 | int minCoins = INT_MAX, i; 15 | for(i = 0; i < coins_len; i++){ 16 | if(c - coins[i] >= 0){ 17 | int curCoins = bruteForce(c-coins[i], coins, coins_len); 18 | if(curCoins < minCoins) 19 | minCoins = curCoins; 20 | } 21 | } 22 | return (minCoins + 1); 23 | } 24 | 25 | int bottomUp(int c, int coins[], int coins_len){ 26 | int cache[c+1], i, j, minCoins, curCoins; 27 | cache[0] = 0; 28 | 29 | for(i = 1; i <= c; i++){ 30 | minCoins = INT_MAX; 31 | for(j = 0; j < coins_len; j++){ 32 | if(i - coins[j] >= 0){ 33 | curCoins = cache[i - coins[j]] + 1; 34 | if(curCoins < minCoins) 35 | minCoins = curCoins; 36 | } 37 | } 38 | cache[i] = minCoins; 39 | } 40 | return cache[c]; 41 | } 42 | 43 | int topDown1(int c, int coins[], int coins_len, int cache[]){ 44 | if(cache[c] >= 0) 45 | return cache[c]; 46 | int minCoins = INT_MAX, i; 47 | for(i = 0; i < coins_len; i++){ 48 | if(c - coins[i] >= 0){ 49 | int curCoins = topDown1(c-coins[i], coins, coins_len, cache); 50 | if(curCoins < minCoins) 51 | minCoins = curCoins; 52 | } 53 | } 54 | cache[c] = minCoins + 1; 55 | return cache[c]; 56 | } 57 | 58 | int topDown(int c, int coins[], int coins_len){ 59 | int cache[c+1], i; 60 | for(i = 1; i <= c; i++) 61 | cache[i] = -1; 62 | cache[0] = 0; 63 | return topDown1(c, coins, coins_len, cache); 64 | } 65 | 66 | void test(){ 67 | int i, inputs[3] = {1,6,49}, input_len = 3, coins[4] = {25,10,5,1}, coins_len = 4; 68 | printf("Brute Force Method:\n"); 69 | for(i = 0; i 9 | #include 10 | 11 | // General function to find the Minimum of two numbers 12 | int MIN(int a, int b){ 13 | if(a <= b) 14 | return a; 15 | return b; 16 | } 17 | 18 | // General function to find the Minimum of two numbers 19 | int MAX(int a, int b){ 20 | if(a >= b) 21 | return a; 22 | return b; 23 | } 24 | 25 | // Recursive function used by bruteForce method 26 | int squareSubmatrixbrute(bool ar[][10], int n, int m, int i, int j){ 27 | if(i == n || j == m || !ar[i][j]) 28 | return 0; 29 | 30 | return 1 + MIN( 31 | MIN(squareSubmatrixbrute(ar, n, m, i+1, j), 32 | squareSubmatrixbrute(ar, n, m, i, j+1)), 33 | squareSubmatrixbrute(ar, n, m, i+1, j+1)); 34 | } 35 | 36 | // Brute force solution, uses squareSubmatrixbrute as a recursive subroutine 37 | int bruteForce(bool ar[][10], int n, int m){ 38 | int max = 0, i, j; 39 | for(i = 0; i < n; i++){ 40 | for(j = 0; j < m; j++){ 41 | if(ar[i][j]){ 42 | max = MAX(max, squareSubmatrixbrute(ar, n, m, i, j)); 43 | } 44 | } 45 | } 46 | return max; 47 | } 48 | 49 | // Recursive function used by topDown method 50 | int squareSubmatrixtopDown(bool ar[][10], int n, int m, int i, int j, int cache[][10]){ 51 | if(i == n || j == m || !ar[i][j]) 52 | return 0; 53 | if(cache[i][j]>0) 54 | return cache[i][j]; 55 | cache[i][j] = 1 + MIN( 56 | MIN(squareSubmatrixtopDown(ar, n, m, i+1, j, cache), 57 | squareSubmatrixtopDown(ar, n, m, i, j+1, cache)), 58 | squareSubmatrixtopDown(ar, n, m, i+1, j+1, cache)); 59 | return cache[i][j]; 60 | } 61 | 62 | // Top-down dynamic programming solution, uses squareSubmatrixtopDown as a recursive subroutine 63 | int topDown(bool ar[][10], int n, int m){ 64 | int cache[10][10], i, j; 65 | for(i = 0; i < n; i++) 66 | for(j = 0; j < m; j++) 67 | cache[i][j] = 0; 68 | int max = 0; 69 | for(i = 0; i < n; i++){ 70 | for(j = 0; j < m; j++){ 71 | if(ar[i][j]){ 72 | max = MAX(max, squareSubmatrixtopDown(ar, n, m, i, j, cache)); 73 | } 74 | } 75 | } 76 | return max; 77 | } 78 | 79 | // Bottom-up dynamic programming solution, makes no recursive calls. 80 | int bottomUp(bool ar[][10], int n, int m){ 81 | int max = 0, cache[10][10], i, j; 82 | for(i = 0; i < n; i++) 83 | for(j = 0; j < m; j++) 84 | cache[i][j] = 0; 85 | for(i = 0; i < n; i++){ 86 | for(j = 0; j < m; j++){ 87 | if(i == 0 || j == 0) 88 | cache[i][j] = (ar[i][j]) ? 1 : 0; 89 | else if(ar[i][j]){ 90 | cache[i][j] = 1 + MIN( 91 | MIN(cache[i-1][j], 92 | cache[i][j-1]), 93 | cache[i-1][j-1]); 94 | } 95 | if(cache[i][j]>max) 96 | max = cache[i][j]; 97 | } 98 | } 99 | return max; 100 | } 101 | 102 | void test(){ 103 | bool input1[][10] = {{true, true, true, false}, 104 | {true, true, true, true}, 105 | {true, true, true, false}}; 106 | bool input2[][10] = {{true, true, true, true, true}, 107 | {true, true, true, true, false}, 108 | {true, true, true, true, false}, 109 | {true, true, true, true, false}, 110 | {true, false, false, false, false}}; 111 | bool input3[][10] = {{true, true, false, false, false}, 112 | {true, true, false, false, false}, 113 | {false, false, true, true, true}, 114 | {false, false, true, true, true}, 115 | {false, false, true, true, true}}; 116 | int i, j; 117 | printf("Test 1:\n"); 118 | for(i = 0; i<3; i++){ 119 | for(j = 0; j<4; j++){ 120 | if(input1[i][j] == true) 121 | printf("T "); 122 | else 123 | printf("F "); 124 | } 125 | printf("\n"); 126 | } 127 | printf("BruteForce(Test 1): %d\n", bruteForce(input1, 3, 4)); 128 | printf("TopDown(Test 1): %d\n", topDown(input1, 3, 4)); 129 | printf("BottomUp(Test 1): %d\n", bottomUp(input1, 3 ,4)); 130 | 131 | printf("\nTest 2:\n"); 132 | for(i = 0; i<5; i++){ 133 | for(j = 0; j<5; j++){ 134 | if(input2[i][j] == true) 135 | printf("T "); 136 | else 137 | printf("F "); 138 | } 139 | printf("\n"); 140 | } 141 | printf("BruteForce(Test 2): %d\n", bruteForce(input2, 5, 5)); 142 | printf("TopDown(Test 2): %d\n", topDown(input2, 5, 5)); 143 | printf("BottomUp(Test 2): %d\n", bottomUp(input2, 5 ,5)); 144 | 145 | printf("\nTest 3:\n"); 146 | for(i = 0; i<5; i++){ 147 | for(j = 0; j<5; j++){ 148 | if(input3[i][j] == true) 149 | printf("T "); 150 | else 151 | printf("F "); 152 | } 153 | printf("\n"); 154 | } 155 | printf("BruteForce(Test 3): %d\n", bruteForce(input3, 5, 5)); 156 | printf("TopDown(Test 3): %d\n", topDown(input3, 5, 5)); 157 | printf("BottomUp(Test 3): %d\n", bottomUp(input3, 5 ,5)); 158 | 159 | 160 | } 161 | 162 | int main(){ 163 | test(); 164 | return 0; 165 | } 166 | 167 | 168 | -------------------------------------------------------------------------------- /Fibonacci.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Fibonacci 3 | * Author: Sam Gavis-Hughson 4 | * Date: 08/01/2017 5 | * 6 | * Given an integer n, write a function that will return the nth Fibonacci number. 7 | * 8 | * eg. 9 | * fib(0) = 0 10 | * fib(1) = 1 11 | * fib(5) = 5 12 | * fib(10) = 55 13 | * 14 | * Execution: javac Fibonacci.java && java Fibonacci 15 | */ 16 | 17 | // We will assume for all solutions that n >= 0 and that int is sufficient 18 | // to hold the result 19 | public class Fibonacci { 20 | 21 | // Compute the nth Fibonacci number recursively 22 | public static int naiveFib(int n) { 23 | if (n < 2) return n; 24 | return naiveFib(n-1) + naiveFib(n-2); 25 | } 26 | 27 | // Compute the nth Fibonacci number recursively. Optimized by caching 28 | // subproblem results 29 | public static int topDownFibOptimized(int n) { 30 | if (n < 2) return n; 31 | 32 | // Create cache and initialize to -1 33 | int[] cache = new int[n+1]; 34 | for (int i = 0; i < cache.length; i++) { 35 | cache[i] = -1; 36 | } 37 | 38 | // Fill initial values in cache 39 | cache[0] = 0; 40 | cache[1] = 1; 41 | return topDownFibOptimized(n, cache); 42 | } 43 | 44 | // Overloaded private method 45 | private static int topDownFibOptimized(int n, int[] cache) { 46 | // If value is set in cache, return 47 | if (cache[n] >= 0) return cache[n]; 48 | 49 | // Otherwise compute and add to cache before returning 50 | cache[n] = topDownFibOptimized(n-1, cache) + topDownFibOptimized(n-2, cache); 51 | return cache[n]; 52 | } 53 | 54 | // Compute the nth Fibonacci number iteratively 55 | public static int bottomUpFib(int n) { 56 | if (n == 0) return 0; 57 | 58 | // Initialize cache 59 | int[] cache = new int[n+1]; 60 | cache[1] = 1; 61 | 62 | // Fill cache iteratively 63 | for (int i = 2; i <= n; i++) { 64 | cache[i] = cache[i-1] + cache[i-2]; 65 | } 66 | 67 | return cache[n]; 68 | } 69 | 70 | // Compute the nth Fibonacci number iteratively with constant space. We only 71 | // need to save the two most recently computed values 72 | public static int bottomUpFibOptimized(int n) { 73 | if (n < 2) return n; 74 | int n1 = 1, n2 = 0; 75 | for (int i = 2; i < n; i++) { 76 | int n0 = n1 + n2; 77 | n2 = n1; 78 | n1 = n0; 79 | } 80 | 81 | return n1 + n2; 82 | } 83 | 84 | // Sample testcases 85 | public static void main(String[] args) { 86 | (new TestCase(0, 0)).run(); 87 | (new TestCase(1, 1)).run(); 88 | (new TestCase(2, 1)).run(); 89 | (new TestCase(5, 5)).run(); 90 | (new TestCase(10, 55)).run(); 91 | System.out.println("Passed all test cases"); 92 | } 93 | 94 | // Class for defining and running test cases 95 | private static class TestCase { 96 | private int input; 97 | private int output; 98 | 99 | private TestCase(int input, int output) { 100 | this.input = input; 101 | this.output = output; 102 | } 103 | 104 | private void run() { 105 | assert naiveFib(input) == output: 106 | "naiveFib failed for input = " + input; 107 | assert topDownFibOptimized(input) == output: 108 | "topDownFibOptimized failed for input = " + input; 109 | assert bottomUpFib(input) == output: 110 | "topDownFib failed for input = " + input; 111 | assert bottomUpFibOptimized(input) == output: 112 | "topDownFib failed for input = " + input; 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /Knapsack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: 0-1 Knapsack 3 | * Author: Sam Gavis-Hughson 4 | * Date: 08/01/2017 5 | * 6 | * Given a list of items with values and weights, as well as a max weight, 7 | * find the maximum value you can generate from items, where the sum of the 8 | * weights is less than or equal to the max. 9 | * 10 | * eg. 11 | * items = {(w:1, v:6), (w:2, v:10), (w:3, v:12)} 12 | * maxWeight = 5 13 | * knapsack(items, maxWeight) = 22 14 | * 15 | * Execution: javac Knapsack.java && java Knapsack 16 | */ 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | public class Knapsack { 22 | 23 | // Public inner class to represent an individual item 24 | public static class Item { 25 | int weight; 26 | int value; 27 | 28 | public Item(int weight, int value) { 29 | this.weight = weight; 30 | this.value = value; 31 | } 32 | } 33 | 34 | // Naive brute force solution. Recursively include or exclude each item 35 | // to try every possible combination 36 | public static int naiveKnapsack(Item[] items, int W) { 37 | return naiveKnapsack(items, W, 0); 38 | } 39 | 40 | // Overloaded recursive function 41 | private static int naiveKnapsack(Item[] items, int W, int i) { 42 | // If we've gone through all the items, return 43 | if (i == items.length) return 0; 44 | // If the item is too big to fill the remaining space, skip it 45 | if (W - items[i].weight < 0) return naiveKnapsack(items, W, i+1); 46 | 47 | // Find the maximum of including and not including the current item 48 | return Math.max(naiveKnapsack(items, W - items[i].weight, i+1) + items[i].value, 49 | naiveKnapsack(items, W, i+1)); 50 | } 51 | 52 | // Top down dynamic programming solution. Cache values in a HashMap because 53 | // the cache may be sparse so we save space 54 | public static int topDownKnapsack(Item[] items, int W) { 55 | // Map: i -> W -> value 56 | Map> cache = new HashMap>(); 57 | return topDownKnapsack(items, W, 0, cache); 58 | } 59 | 60 | // Overloaded recursive function 61 | private static int topDownKnapsack(Item[] items, int W, int i, Map> cache) { 62 | if (i == items.length) return 0; 63 | 64 | // Check if the value is in the cache 65 | if (!cache.containsKey(i)) cache.put(i, new HashMap()); 66 | Integer cached = cache.get(i).get(W); 67 | if (cached != null) return cached; 68 | 69 | // If not, compute the item and add it to the cache 70 | int toReturn; 71 | if (W - items[i].weight < 0) { 72 | toReturn = topDownKnapsack(items, W, i+1, cache); 73 | } else { 74 | toReturn = Math.max(topDownKnapsack(items, W - items[i].weight, i+1, cache) + items[i].value, 75 | topDownKnapsack(items, W, i+1, cache)); 76 | } 77 | cache.get(i).put(W, toReturn); 78 | return toReturn; 79 | } 80 | 81 | // Iterative bottom up solution. 82 | public static int bottomUpKnapsack(Item[] items, int W) { 83 | // Initialize cache 84 | int[][] cache = new int[items.length + 1][W + 1]; 85 | // For each item and weight, compute the max value of the items up to 86 | // that item that doesn't go over W weight 87 | for (int i = 1; i <= items.length; i++) { 88 | for (int j = 0; j <= W; j++) { 89 | if (items[i-1].weight > j) cache[i][j] = cache[i-1][j]; 90 | else cache[i][j] = Math.max(cache[i-1][j], cache[i-1][j-items[i-1].weight] + items[i-1].value); 91 | } 92 | } 93 | 94 | return cache[items.length][W]; 95 | } 96 | 97 | // Optimized bottom up solution with 1D cache. Same as before but only save 98 | // the cache of i-1 and not all values of i. 99 | public static int bottomUpOptimizedKnapsack(Item[] items, int W) { 100 | int[] cache = new int[W + 1]; 101 | for (Item i : items) { 102 | int[] newCache = new int[W + 1]; 103 | for (int j = 0; j <= W; j++) { 104 | if (i.weight > j) newCache[j] = cache[j]; 105 | else newCache[j] = Math.max(cache[j], cache[j - i.weight] + i.value); 106 | } 107 | cache = newCache; 108 | } 109 | 110 | return cache[W]; 111 | } 112 | 113 | // Sample testcases 114 | public static void main(String[] args) { 115 | (new TestCase(new Item[]{}, 0, 0)).run(); 116 | (new TestCase(new Item[]{ 117 | new Item(4, 5), 118 | new Item(1, 8), 119 | new Item(2, 4), 120 | new Item(3, 0), 121 | new Item(2, 5), 122 | new Item(2, 3) 123 | }, 3, 13)).run(); 124 | (new TestCase(new Item[]{ 125 | new Item(4, 5), 126 | new Item(1, 8), 127 | new Item(2, 4), 128 | new Item(3, 0), 129 | new Item(2, 5), 130 | new Item(2, 3) 131 | }, 8, 20)).run(); 132 | 133 | System.out.println("Passed all test cases"); 134 | } 135 | 136 | // Class for defining and running test cases 137 | private static class TestCase { 138 | private Item[] items; 139 | private int weight; 140 | private int output; 141 | 142 | private TestCase(Item[] items, int weight, int output) { 143 | this.items = items; 144 | this.weight = weight; 145 | this.output = output; 146 | } 147 | 148 | private void run() { 149 | assert naiveKnapsack(items, weight) == output: 150 | "naiveKnapsack failed for items = " + itemsString() + ", weight = " + weight; 151 | assert topDownKnapsack(items, weight) == output: 152 | "topDownKnapsack failed for items = " + itemsString() + ", weight = " + weight; 153 | assert bottomUpKnapsack(items, weight) == output: 154 | "bottomUpKnapsack failed for items = " + itemsString() + ", weight = " + weight; 155 | assert bottomUpOptimizedKnapsack(items, weight) == output: 156 | "bottomUpOptimizedKnapsack failed for items = " + itemsString() + ", weight = " + weight; 157 | } 158 | 159 | private String itemsString() { 160 | StringBuilder sb = new StringBuilder(); 161 | sb.append("["); 162 | for (int i = 0; i < items.length; i++) { 163 | Item item = items[i]; 164 | sb.append("{w:" + item.weight + ",v:" + item.value + "}"); 165 | if (i != items.length - 1) sb.append(","); 166 | } 167 | sb.append("]"); 168 | 169 | return sb.toString(); 170 | } 171 | } 172 | } -------------------------------------------------------------------------------- /MakingChange.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samgh/DynamicProgrammingEbook/cf9442bddad7d1989313fd7939d6e6edeedf16fd/MakingChange.java -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dynamic Programming for Interviews Solutions 2 | [Dynamic Programming for Interviews](http://www.dynamicprogrammingbook.com) is a free ebook about dynamic programming. This repo contains working, tested code for the solutions in Dynamic Programming for Interviews. 3 | 4 | ### Contributing 5 | I would love to compile solutions to all of the problems here, as well as offer solutions in different languages. Currently we only have Java solutions but Python, C, or any other languages would be most welcome. Just create a pull request with your changes. And make sure your code includes at least a few tests! -------------------------------------------------------------------------------- /SquareSubmatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Square submatrix 3 | * Author: Sam Gavis-Hughson 4 | * Date: 08/01/2017 5 | * 6 | * Given a 2D boolean array, find the largest square subarray of true values. 7 | * The return value should be the side length of the largest square subarray 8 | * subarray. 9 | * 10 | * eg. 11 | * squareSubmatrix([false, true, false, false]) = 2 12 | * [true, TRUE, TRUE, true ] 13 | * [false, TRUE, TRUE, false] 14 | * (the solution is the submatrix in all caps) 15 | * 16 | * Execution: javac SquareSubmatrix.java && java SquareSubmatrix 17 | */ 18 | 19 | import java.util.Arrays; 20 | 21 | public class SquareSubmatrix { 22 | // Brute force solution. From each cell see what is the biggest square 23 | // submatrix for which it is the upper left-hand corner 24 | public static int naiveSquareSubmatrix(boolean[][] arr) { 25 | int max = 0; 26 | // Compute recursively for each cell what it is the upper left corner of 27 | for (int i = 0; i < arr.length; i++) { 28 | for (int j = 0; j < arr[0].length; j++) { 29 | if (arr[i][j]) max = Math.max(max, naiveSquareSubmatrix(arr, i, j)); 30 | } 31 | } 32 | 33 | return max; 34 | } 35 | 36 | // Overloaded recursive function 37 | private static int naiveSquareSubmatrix(boolean[][] arr, int i, int j) { 38 | // If we get to the bottom or right of the matrix, we can't go any 39 | // further 40 | if (i == arr.length || j == arr[0].length) return 0; 41 | 42 | // If the cell is False then it is not part of a valid submatrix 43 | if (!arr[i][j]) return 0; 44 | 45 | // Find the size of the right, bottom, and bottom right submatrices and 46 | // add 1 to the minimum of those 3 to get the result 47 | return 1 + Math.min(Math.min(naiveSquareSubmatrix(arr, i+1, j), naiveSquareSubmatrix(arr, i, j+1)), 48 | naiveSquareSubmatrix(arr, i+1, j+1)); 49 | } 50 | 51 | // Top down dynamic programming solution. Cache the values as we compute 52 | // them to avoid repeating computations 53 | public static int topDownSquareSubmatrix(boolean[][] arr) { 54 | // Initialize cache. Don't need to initialize to -1 because the only 55 | // cells that will be 0 are ones that are False and we want to skip 56 | // those ones anyway 57 | int[][] cache = new int[arr.length][arr[0].length]; 58 | int max = 0; 59 | for (int i = 0; i < arr.length; i++) { 60 | for (int j = 0; j < arr[0].length; j++) { 61 | if (arr[i][j]) max = Math.max(max, topDownSquareSubmatrix(arr, i, j, cache)); 62 | } 63 | } 64 | 65 | return max; 66 | } 67 | 68 | // Overloaded recursive function 69 | private static int topDownSquareSubmatrix(boolean[][] arr, int i, int j, int[][] cache) { 70 | if (i == arr.length || j == arr[0].length) return 0; 71 | if (!arr[i][j]) return 0; 72 | 73 | // If the value is set in the cache return it. Otherwise compute and 74 | // save to cache 75 | if (cache[i][j] > 0) return cache[i][j]; 76 | cache[i][j] = 1 + Math.min(Math.min(topDownSquareSubmatrix(arr, i+1, j, cache), topDownSquareSubmatrix(arr, i, j+1, cache)), 77 | topDownSquareSubmatrix(arr, i+1, j+1, cache)); 78 | return cache[i][j]; 79 | } 80 | 81 | // Bottom up solution. Start from the upper left-hand corner and compute 82 | // progressively larger submatrices 83 | public static int bottomUpSquareSubmatrix(boolean[][] arr) { 84 | int max = 0; 85 | // Initialize cache 86 | int[][] cache = new int[arr.length][arr[0].length]; 87 | // Iterate over the matrix to compute all values 88 | for (int i = 0; i < cache.length; i++) { 89 | for (int j = 0; j < cache[0].length; j++) { 90 | // If we are in the first row or column then the value is just 91 | // 1 if that cell is true and 0 otherwise. In other rows and 92 | // columns, need to look up and to the left 93 | if (i == 0 || j == 0) { 94 | cache[i][j] = arr[i][j] ? 1 : 0; 95 | } else if (arr[i][j]) { 96 | cache[i][j] = Math.min(Math.min(cache[i][j-1], 97 | cache[i-1][j]), 98 | cache[i-1][j-1]) + 1; 99 | } 100 | if (cache[i][j] > max) max = cache[i][j]; 101 | } 102 | } 103 | 104 | return max; 105 | } 106 | // Sample testcases 107 | public static void main(String[] args) { 108 | (new TestCase(new boolean[][]{new boolean[]{true}}, 1)).run(); 109 | (new TestCase(new boolean[][]{new boolean[]{false}}, 0)).run(); 110 | (new TestCase(new boolean[][]{ 111 | new boolean[]{true, true, true, false}, 112 | new boolean[]{false, true, true, true}, 113 | new boolean[]{true, true, true, true}}, 2)).run(); 114 | (new TestCase(new boolean[][]{ 115 | new boolean[]{true, true, true, true}, 116 | new boolean[]{false, true, true, true}, 117 | new boolean[]{true, true, true, true}}, 3)).run(); 118 | System.out.println("Passed all test cases"); 119 | } 120 | 121 | // Class for defining and running test cases 122 | private static class TestCase { 123 | private boolean[][] input; 124 | private int output; 125 | 126 | private TestCase(boolean[][] input, int output) { 127 | this.input = input; 128 | this.output = output; 129 | } 130 | 131 | private void run() { 132 | assert naiveSquareSubmatrix(input) == output: 133 | "naiveSquareSubmatrix failed for input = " + inputString(); 134 | assert topDownSquareSubmatrix(input) == output: 135 | "topDownSquareSubmatrix failed for input = " + inputString(); 136 | assert bottomUpSquareSubmatrix(input) == output: 137 | "bottomUpSquareSubmatrix failed for input = " + inputString(); 138 | } 139 | 140 | private String inputString() { 141 | StringBuilder sb = new StringBuilder(); 142 | for (int i = 0; i < input.length; i++) { 143 | sb.append(Arrays.toString(input[i])); 144 | if (i != input.length - 1) sb.append('\n'); 145 | } 146 | return sb.toString(); 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /TargetSum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Target Sum 3 | * Author: Sam Gavis-Hughson 4 | * Date: 08/01/2017 5 | * 6 | * Given an array of integers, nums and a target value T, find the number of 7 | * ways that you can add and subtract the values in nums to add up to T. 8 | * 9 | * eg. 10 | * nums = {1, 1, 1, 1, 1} 11 | * target = 3 12 | * 13 | * 1 + 1 + 1 + 1 - 1 14 | * 1 + 1 + 1 - 1 + 1 15 | * 1 + 1 - 1 + 1 + 1 16 | * 1 - 1 + 1 + 1 + 1 17 | * -1 + 1 + 1 + 1 + 1 18 | * 19 | * targetSum(nums, target) = 5 20 | * 21 | * Execution: javac TargetSum.java && java TargetSum 22 | */ 23 | 24 | import java.util.Arrays; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | public class TargetSum { 29 | 30 | // Naive brute force solution. Find every possible combination 31 | public static int naiveTargetSum(int[] nums, int T) { 32 | return naiveTargetSum(nums, T, 0, 0); 33 | } 34 | 35 | // Overloaded recursive function 36 | private static int naiveTargetSum(int[] nums, int T, int i, int sum) { 37 | // When we've gone through every item, see if we've reached our target sum 38 | if (i == nums.length) { 39 | return sum == T ? 1 : 0; 40 | } 41 | 42 | // Combine the number of possibilites by adding and subtracting the 43 | // current value 44 | return naiveTargetSum(nums, T, i+1, sum + nums[i]) + naiveTargetSum(nums, T, i+1, sum - nums[i]); 45 | } 46 | 47 | // Top down dynamic programming solution. Like with 0-1 Knapsack, we use a 48 | // HashMap to save on space 49 | public static int topDownTargetSum(int[] nums, int T) { 50 | // Map: i -> sum -> value 51 | Map> cache = new HashMap>(); 52 | return topDownTargetSum(nums, T, 0, 0, cache); 53 | } 54 | 55 | // Overloaded recursive function 56 | private static int topDownTargetSum(int[] nums, int T, int i, int sum, Map> cache) { 57 | if (i == nums.length) { 58 | return sum == T ? 1 : 0; 59 | } 60 | 61 | // Check the cache and return value if we get a hit 62 | if (!cache.containsKey(i)) cache.put(i, new HashMap()); 63 | Integer cached = cache.get(i).get(sum); 64 | if (cached != null) return cached; 65 | 66 | // If we didn't hit in the cache, compute the value and store to cache 67 | int toReturn = topDownTargetSum(nums, T, i+1, sum + nums[i], cache) + 68 | topDownTargetSum(nums, T, i+1, sum - nums[i], cache); 69 | cache.get(i).put(sum, toReturn); 70 | return toReturn; 71 | } 72 | 73 | // Bottom up dynamic programming solution 74 | public static int bottomUpTargetSum(int[] nums, int T) { 75 | int sum = 0; 76 | 77 | // Our cache has to range from -sum(nums) to sum(nums), so we offset 78 | // everything by sum 79 | for (int num : nums) sum += num; 80 | int[][] cache = new int[nums.length + 1][2 * sum + 1]; 81 | 82 | if (sum == 0) return 0; 83 | 84 | // Initialize i=0, T=0 85 | cache[0][sum] = 1; 86 | 87 | // Iterate over the previous row and update the current row 88 | for (int i = 1; i <= nums.length; i++) { 89 | for (int j = 0; j < 2 * sum + 1; j++) { 90 | int prev = cache[i-1][j]; 91 | if (prev != 0) { 92 | cache[i][j - nums[i-1]] += prev; 93 | cache[i][j + nums[i-1]] += prev; 94 | } 95 | } 96 | } 97 | 98 | return cache[nums.length][sum + T]; 99 | } 100 | 101 | // Sample testcases 102 | public static void main(String[] args) { 103 | (new TestCase(new int[]{}, 1, 0)).run(); 104 | (new TestCase(new int[]{1, 1, 1, 1, 1}, 3, 5)).run(); 105 | (new TestCase(new int[]{1, 1, 1}, 1, 3)).run(); 106 | System.out.println("Passed all test cases"); 107 | } 108 | 109 | // Class for defining and running test cases 110 | private static class TestCase { 111 | private int[] nums; 112 | private int target; 113 | private int output; 114 | 115 | private TestCase(int[] nums, int target, int output) { 116 | this.nums = nums; 117 | this.target = target; 118 | this.output = output; 119 | } 120 | 121 | private void run() { 122 | assert naiveTargetSum(nums, target) == output: 123 | "naiveTargetSum failed for nums = " + Arrays.toString(nums) + ", target = " + target; 124 | assert topDownTargetSum(nums, target) == output: 125 | "topDownTargetSum failed for nums = " + Arrays.toString(nums) + ", target = " + target; 126 | assert bottomUpTargetSum(nums, target) == output: 127 | "bottomUpTargetSum failed for nums = " + Arrays.toString(nums) + ", target = " + target; 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /cpp/Fibonacci.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FIbonacci.cpp 3 | // DynamicProgrammingEbook 4 | // 5 | // Created by Solomon Kinard on 11/16/18. 6 | // Copyright © 2018 Solomon Kinard. All rights reserved. 7 | // 8 | 9 | 10 | #include 11 | #include 12 | 13 | namespace Fibonacci { 14 | int fib(int n) { 15 | if (n < 2) return n; 16 | int n1 = 1, n2 = 0; 17 | for (int i = 2; i < n; i++) { 18 | int n0 = n1 + n2; 19 | n2 = n1; 20 | n1 = n0; 21 | } 22 | return n1 + n2; 23 | } 24 | 25 | void test() { 26 | for (int i=-2; i<9; i++) { 27 | printf("i=%d, fib(i)=%d\n", i, fib(i)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cpp/Knapsack.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Knapsack.cpp 3 | // DynamicProgrammingEbook 4 | // 5 | // Created by Solomon Kinard on 11/16/18. 6 | // Copyright © 2018 Solomon Kinard. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | using std::vector; 14 | using std::tuple; 15 | 16 | namespace Knapsack { 17 | class Item { 18 | public: 19 | int weight; 20 | int value; 21 | Item(int w, int v): weight(w), value(v) {} 22 | }; 23 | 24 | int knapsack(vector items, int w, int i) { 25 | if (i == items.size()) return 0; 26 | if (w - items[i].weight < 0) return knapsack(items, w, i+1); 27 | return std::max( 28 | knapsack(items, w - items[i].weight, i+1) + items[i].value, knapsack(items, w, i+1) 29 | ); 30 | } 31 | 32 | int knapsack(vector &items, int w) { 33 | return knapsack(items, w, 0); 34 | } 35 | 36 | void test() { 37 | vector items{ 38 | {2,6}, 39 | {2,10}, 40 | {3,12}, 41 | }; 42 | int max_weight = 5; 43 | int expected = 22; 44 | int output = knapsack(items, max_weight); 45 | assert(expected == output); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cpp/MakingChange.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MakingChange.cpp 3 | // DynamicProgrammingEbook 4 | // 5 | // Created by Solomon Kinard on 11/16/18. 6 | // Copyright © 2018 Solomon Kinard. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | 12 | using std::vector; 13 | 14 | namespace MakingChange { 15 | vector coins{25,10,5,1}; 16 | 17 | int makeChange(int c) { 18 | vector cache(c+1); 19 | for (int i=1; i<=c; i++) { 20 | int minCoins = INT_MAX; 21 | for (int coin : coins) { 22 | if (i - coin >= 0) { 23 | int currCoins = cache[i-coin] + 1; 24 | if (currCoins < minCoins) { 25 | minCoins = currCoins; 26 | } 27 | } 28 | } 29 | cache[i] = minCoins; 30 | } 31 | return cache[c]; 32 | } 33 | 34 | void test() { 35 | vector inputs{1,6,49}; 36 | for (int input : inputs) { 37 | printf("makeChange(%d): %d\n", input, makeChange(input)); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cpp/SquareSubmatrix.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SquareSubmatrix.cpp 3 | // DynamicProgrammingEbook 4 | // 5 | // Created by Solomon Kinard on 11/16/18. 6 | // Copyright © 2018 Solomon Kinard. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | 12 | using std::vector; 13 | using std::min; 14 | 15 | namespace SquareSubmatrix { 16 | int squareSubmatrix(vector> &vec) { 17 | int max = 0; 18 | int n = (int)vec.size(); 19 | vector> cache(n, vector(n)); 20 | for (int i=0; i max) { 28 | max = cache[i][j]; 29 | } 30 | } 31 | } 32 | return max; 33 | } 34 | 35 | void test() { 36 | vector> grid = { 37 | {1,1,1,1,1}, 38 | {1,1,1,1,0}, 39 | {1,1,1,1,0}, 40 | {1,1,1,1,0}, 41 | {1,0,0,0,0}, 42 | }; 43 | int output = squareSubmatrix(grid); 44 | int expected = 4; 45 | assert(output == expected); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cpp/TargetSum.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // TargetSum.cpp 3 | // DynamicProgrammingEbook 4 | // 5 | // Created by Solomon Kinard on 11/16/18. 6 | // Copyright © 2018 Solomon Kinard. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | 12 | using std::vector; 13 | 14 | namespace TargetSum { 15 | int targetSum(vector nums, int t) { 16 | int sum = 0; 17 | for (int num : nums) sum += num; 18 | vector> cache(nums.size()+1, vector(2*sum+1)); 19 | if (sum == 0) return 0; 20 | cache[0][sum] = 1; 21 | for (int i=1; i<=nums.size(); i++) { 22 | for (int j=0; j<2*sum+1; j++) { 23 | int prev = cache[i-1][j]; 24 | if (prev != 0) { 25 | cache[i][j-nums[i-1]] += prev; 26 | cache[i][j+nums[i-1]] += prev; 27 | } 28 | } 29 | } 30 | return cache[nums.size()][sum+t]; 31 | } 32 | 33 | void test() { 34 | vector nums{1,1,1,1,1}; 35 | int t = 3; 36 | int expected = 5; 37 | int output = targetSum(nums, t); 38 | assert(output == expected); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /python/Fibonacci.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # Copyright (c) 2018 Nikolay Derkach 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | 24 | import unittest 25 | 26 | 27 | class Fibonacci(object): 28 | def naive_fib(n): 29 | """ 30 | Naive implementation with recursive calls 31 | """ 32 | if n < 2: 33 | return n 34 | return Fibonacci.naive_fib(n-2) + Fibonacci.naive_fib(n-1) 35 | 36 | def top_down_fib_optimized(n): 37 | """ 38 | Compute the nth Fibonacci number recursively. Optimized by caching 39 | subproblem results 40 | """ 41 | 42 | if n < 2: 43 | return n 44 | 45 | cache = [-1]*(n+1) 46 | cache[0] = 0 47 | cache[1] = 1 48 | 49 | return Fibonacci.__top_down_fib_optimized(n, cache) 50 | 51 | def __top_down_fib_optimized(n, cache): 52 | if cache[n] >= 0: 53 | return cache[n] 54 | 55 | cache[n] = Fibonacci.__top_down_fib_optimized( 56 | n-1, cache) + Fibonacci.__top_down_fib_optimized(n-2, cache) 57 | return cache[n] 58 | 59 | def bottom_up_fib(n): 60 | """ 61 | Compute the nth Fibonacci number iteratively 62 | """ 63 | 64 | if n == 0: 65 | return 0 66 | 67 | cache = [0]*(n+1) 68 | cache[1] = 1 69 | 70 | for i in range(2, n+1): 71 | cache[i] = cache[i-1] + cache[i-2] 72 | 73 | return cache[n] 74 | 75 | def bottom_up_fib_optimized(n): 76 | """ 77 | Compute the nth Fibonacci number iteratively with constant space. We only 78 | need to save the two most recently computed values 79 | """ 80 | 81 | if n < 2: 82 | return n 83 | 84 | n1, n2 = 1, 0 85 | 86 | for i in range(2, n+1): 87 | n0 = n1 + n2 88 | n2 = n1 89 | n1 = n0 90 | 91 | return n0 92 | 93 | 94 | class TestFibonacci(unittest.TestCase): 95 | 96 | def setUp(self): 97 | self.testcases = [(0, 0), (1, 1), (2, 1), (5, 5), (10, 55)] 98 | 99 | def test_naive(self): 100 | for value, expected in self.testcases: 101 | with self.subTest(value=value): 102 | self.assertEqual(Fibonacci.naive_fib(value), expected) 103 | 104 | def test_top_down(self): 105 | for value, expected in self.testcases: 106 | with self.subTest(value=value): 107 | self.assertEqual( 108 | Fibonacci.top_down_fib_optimized(value), expected) 109 | 110 | def test_bottom_up(self): 111 | for value, expected in self.testcases: 112 | with self.subTest(value=value): 113 | self.assertEqual(Fibonacci.bottom_up_fib(value), expected) 114 | 115 | def test_bottom_up_optimized(self): 116 | for value, expected in self.testcases: 117 | with self.subTest(value=value): 118 | self.assertEqual( 119 | Fibonacci.bottom_up_fib_optimized(value), expected) 120 | 121 | 122 | if __name__ == '__main__': 123 | unittest.main() 124 | -------------------------------------------------------------------------------- /python/Knapsack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # Copyright (c) 2018 Nikolay Derkach 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | 24 | import unittest 25 | 26 | 27 | class Item(object): 28 | def __init__(self, weight, value): 29 | self.weight = weight 30 | self.value = value 31 | 32 | 33 | class Knapsack(object): 34 | 35 | def naive_knapsack(items, W): 36 | return Knapsack.__naive_knapsack(items, W, 0) 37 | 38 | def __naive_knapsack(items, W, i): 39 | # If we've gone through all the items, return 40 | if i == len(items): 41 | return 0 42 | 43 | # If the item is too big to fill the remaining space, skip it 44 | if W - items[i].weight < 0: 45 | return Knapsack.__naive_knapsack(items, W, i+1) 46 | 47 | # Find the maximum of including and not including the current item 48 | return max(Knapsack.__naive_knapsack(items, W - items[i].weight, i+1) + items[i].value, Knapsack.__naive_knapsack(items, W, i+1)) 49 | 50 | def top_down_knapsack(items, W): 51 | cache = {} 52 | return Knapsack.__top_down_knapsack(items, W, 0, cache) 53 | 54 | def __top_down_knapsack(items, W, i, cache): 55 | if i == len(items): 56 | return 0 57 | 58 | # Check if the value is in the cache 59 | if i not in cache: 60 | cache[i] = {} 61 | 62 | cached = cache[i].get(W) 63 | if cached: 64 | return cached 65 | 66 | # If not, compute the item and add it to the cache 67 | if W - items[i].weight < 0: 68 | # exclude item 69 | return_value = Knapsack.__top_down_knapsack(items, W, i+1, cache) 70 | else: 71 | return_value = max(Knapsack.__top_down_knapsack( 72 | items, W - items[i].weight, i+1, cache) + items[i].value, Knapsack.__top_down_knapsack(items, W, i+1, cache)) 73 | 74 | cache[i][W] = return_value 75 | 76 | return return_value 77 | 78 | def bottom_up_knapsack(items, W): 79 | """ 80 | Iterative bottom up solution 81 | """ 82 | cache = [[0]*(W+1) for _ in range(len(items) + 1)] 83 | 84 | # For each item and weight, compute the max value of the items up to 85 | # that item that doesn't go over W weight 86 | 87 | for i in range(1, len(items)+1): 88 | for j in range(W+1): 89 | if items[i-1].weight > j: 90 | cache[i][j] = cache[i-1][j] 91 | else: 92 | cache[i][j] = max(cache[i-1][j], cache[i-1] 93 | [j-items[i-1].weight] + items[i-1].value) 94 | 95 | return cache[len(items)][W] 96 | 97 | def bottom_up_knapsack_optimized(items, W): 98 | """ 99 | Optimized bottom up solution with 1D cache. Same as before but only save 100 | the cache of i-1 and not all values of i 101 | """ 102 | 103 | cache = [0]*(W+1) 104 | 105 | for item in items: 106 | new_cache = [0]*(W+1) 107 | for j in range(W+1): 108 | if item.weight > j: 109 | new_cache[j] = cache[j] 110 | else: 111 | new_cache[j] = max( 112 | cache[j], cache[j-item.weight] + item.value) 113 | 114 | cache = new_cache 115 | 116 | return cache[W] 117 | 118 | 119 | class TestKnapsack(unittest.TestCase): 120 | 121 | def setUp(self): 122 | self.testcases = [([], 0, 0), ([Item(4, 5), Item(1, 8), Item(2, 4), Item(3, 0), Item(2, 5), Item( 123 | 2, 3)], 3, 13), ([Item(4, 5), Item(1, 8), Item(2, 4), Item(3, 0), Item(2, 5), Item(2, 3)], 8, 20)] 124 | 125 | def test_naive_knapsack(self): 126 | for items, weight, expected in self.testcases: 127 | with self.subTest(value=(items, weight)): 128 | self.assertEqual( 129 | Knapsack.naive_knapsack(items, weight), expected) 130 | 131 | def test_top_down_knapsack(self): 132 | for items, weight, expected in self.testcases: 133 | with self.subTest(value=(items, weight)): 134 | self.assertEqual( 135 | Knapsack.top_down_knapsack(items, weight), expected) 136 | 137 | def test_bottom_up_knapsack(self): 138 | for items, weight, expected in self.testcases: 139 | with self.subTest(value=(items, weight)): 140 | self.assertEqual( 141 | Knapsack.bottom_up_knapsack(items, weight), expected) 142 | 143 | def test_bottom_up_knapsack_optimized(self): 144 | for items, weight, expected in self.testcases: 145 | with self.subTest(value=(items, weight)): 146 | self.assertEqual( 147 | Knapsack.bottom_up_knapsack_optimized(items, weight), expected) 148 | 149 | 150 | if __name__ == '__main__': 151 | unittest.main() 152 | -------------------------------------------------------------------------------- /python/MakingChange.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # Copyright (c) 2018 Nikolay Derkach 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | 24 | import unittest 25 | 26 | 27 | class MakingChange(object): 28 | 29 | def __init__(self, coins): 30 | self.coins = coins 31 | 32 | def greedy_making_change(self, c): 33 | """ 34 | Greedy algorithm. Take the largest coin possible each time. Doesn't work 35 | for all coin systems. We also assume that the coins are in descending 36 | order 37 | """ 38 | total_coins = 0 39 | # Remove the largest coin from c that doesn't make c negative 40 | for coin in self.coins: 41 | while c - coin >= 0: 42 | total_coins += 1 43 | c -= coin 44 | return total_coins 45 | 46 | def optimal_greedy_making_change(self, c): 47 | """ 48 | Optimized greedy algorithm. By using mods, we can solve this in constant 49 | time but this solution has the same limitations 50 | """ 51 | total_coins = 0 52 | # Find how many of each coin can go into the remaining total 53 | for coin in self.coins: 54 | total_coins += c // coin 55 | c %= coin 56 | return total_coins 57 | 58 | def naive_making_change(self, c): 59 | """ 60 | Brute force solution. Go through every possible combination of coins that 61 | sum up to c to find the minimum number 62 | """ 63 | if c == 0: 64 | return 0 65 | 66 | min_coins = float('inf') 67 | 68 | # Try removing each coin from the total and see how many more coins 69 | # are required 70 | for coin in self.coins: 71 | # Skip a coin if it's value is greater than the amount remaining 72 | if c - coin >= 0: 73 | cur_min_coins = self.naive_making_change(c - coin) 74 | min_coins = min(min_coins, cur_min_coins) 75 | 76 | # Our recursive call removes one coin from the amount, so add it back 77 | return min_coins + 1 78 | 79 | def top_down_making_change_optimized(self, c): 80 | """ 81 | Top down dynamic solution. Cache the values as we compute them 82 | """ 83 | 84 | cache = [-1]*(c+1) 85 | cache[0] = 0 86 | 87 | return self.__top_down_making_change_optimized(c, cache) 88 | 89 | def __top_down_making_change_optimized(self, c, cache): 90 | # Return the value if it's in the cache 91 | if cache[c] >= 0: 92 | return cache[c] 93 | 94 | min_coins = float('inf') 95 | 96 | # Try removing each coin from the total and see how many more coins 97 | # are required 98 | for coin in self.coins: 99 | # Skip a coin if it's value is greater than the amount remaining 100 | if c - coin >= 0: 101 | cur_min_coins = self.__top_down_making_change_optimized( 102 | c - coin, cache) 103 | min_coins = min(min_coins, cur_min_coins) 104 | 105 | # Save the value into the cache 106 | cache[c] = min_coins + 1 107 | return cache[c] 108 | 109 | def bottom_up_making_change(self, c): 110 | cache = [0]*(c+1) 111 | for i in range(1, c+1): 112 | min_coins = float('inf') 113 | 114 | # Try removing each coin from the total and see which requires 115 | # the fewest additional coins 116 | 117 | for coin in self.coins: 118 | if i - coin >= 0: 119 | cur_min_coins = cache[i-coin] + 1 120 | min_coins = min(min_coins, cur_min_coins) 121 | 122 | cache[i] = min_coins 123 | return cache[c] 124 | 125 | 126 | class TestMakingChange(unittest.TestCase): 127 | 128 | def setUp(self): 129 | self.american_coins = [25, 10, 5, 1] 130 | self.random_coins = [10, 6, 1] 131 | 132 | self.testcases = [(self.american_coins, 1, 1), (self.american_coins, 6, 2), (self.american_coins, 47, 5), ( 133 | self.random_coins, 1, 1), (self.random_coins, 8, 3), (self.random_coins, 11, 2), (self.random_coins, 12, 2)] 134 | 135 | def test_naive_making_change(self): 136 | for coins, value, expected in self.testcases: 137 | making_change = MakingChange(coins) 138 | with self.subTest(value=(coins, value)): 139 | self.assertEqual( 140 | making_change.naive_making_change(value), expected) 141 | 142 | def test_top_down_making_change_optimized(self): 143 | for coins, value, expected in self.testcases: 144 | making_change = MakingChange(coins) 145 | with self.subTest(value=(coins, value)): 146 | self.assertEqual( 147 | making_change.top_down_making_change_optimized(value), expected) 148 | 149 | def test_bottom_up_making_change(self): 150 | for coins, value, expected in self.testcases: 151 | making_change = MakingChange(coins) 152 | with self.subTest(value=(coins, value)): 153 | self.assertEqual( 154 | making_change.bottom_up_making_change(value), expected) 155 | 156 | 157 | if __name__ == '__main__': 158 | unittest.main() 159 | -------------------------------------------------------------------------------- /python/SquareSubmatrix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # Copyright (c) 2018 Nikolay Derkach 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | 24 | import unittest 25 | 26 | 27 | class SquareSubmatrix(object): 28 | 29 | def naive_square_submatrix(arr): 30 | """ 31 | Brute force solution. From each cell see what is the biggest square 32 | submatrix for which it is the upper left-hand corner 33 | """ 34 | max_size = 0 35 | # Compute recursively for each cell what it is the upper left corner of 36 | for row in range(len(arr)): 37 | for column in range(len(arr[row])): 38 | if arr[row][column]: 39 | max_size = max( 40 | max_size, SquareSubmatrix.__naive_square_submatrix(arr, row, column)) 41 | 42 | return max_size 43 | 44 | def __naive_square_submatrix(arr, row, column): 45 | # If we get to the bottom or right of the matrix, we can't go any 46 | # further 47 | if row == len(arr) or column == len(arr[0]): 48 | return 0 49 | 50 | # If the cell is False then it is not part of a valid submatrix 51 | if not arr[row][column]: 52 | return 0 53 | 54 | # Find the size of the right, bottom, and bottom right submatrices and 55 | # add 1 to the minimum of those 3 to get the result 56 | return 1 + min(SquareSubmatrix.__naive_square_submatrix(arr, row+1, column), SquareSubmatrix.__naive_square_submatrix(arr, row, column+1), SquareSubmatrix.__naive_square_submatrix(arr, row+1, column+1)) 57 | 58 | def top_down_square_submatrix(arr): 59 | """ 60 | Top down dynamic programming solution. Cache the values as we compute 61 | them to avoid repeating computations 62 | """ 63 | cache = [[0]*len(row) for row in arr] 64 | 65 | max_size = 0 66 | # Compute recursively for each cell what it is the upper left corner of 67 | for row in range(len(arr)): 68 | for column in range(len(arr[row])): 69 | if arr[row][column]: 70 | max_size = max( 71 | max_size, SquareSubmatrix.__top_down_square_submatrix(arr, row, column, cache)) 72 | 73 | return max_size 74 | 75 | def __top_down_square_submatrix(arr, row, column, cache): 76 | # If we get to the bottom or right of the matrix, we can't go any 77 | # further 78 | if row == len(arr) or column == len(arr[0]): 79 | return 0 80 | 81 | # If the cell is False then it is not part of a valid submatrix 82 | if not arr[row][column]: 83 | return 0 84 | 85 | # If the value is set in the cache return it. Otherwise compute and 86 | # save to cache 87 | if cache[row][column]: 88 | return cache[row][column] 89 | 90 | # Find the size of the right, bottom, and bottom right submatrices and 91 | # add 1 to the minimum of those 3 to get the result 92 | cache[row][column] = 1 + min(SquareSubmatrix.__top_down_square_submatrix(arr, row+1, column, cache), SquareSubmatrix.__top_down_square_submatrix( 93 | arr, row, column+1, cache), SquareSubmatrix.__top_down_square_submatrix(arr, row+1, column+1, cache)) 94 | 95 | return cache[row][column] 96 | 97 | def bottom_up_square_submatrix(arr): 98 | # Initialize cache 99 | cache = [[0]*len(row) for row in arr] 100 | 101 | max_size = 0 102 | 103 | # Iterate over the matrix to compute all values 104 | for row in range(len(arr)): 105 | for column in range(len(arr[row])): 106 | # If we are in the first row or column then the value is just 107 | # 1 if that cell is true and 0 otherwise. In other rows and 108 | # columns, need to look up and to the left 109 | if row == 0 or column == 0: 110 | cache[row][column] = int(arr[row][column]) 111 | else: 112 | cache[row][column] = 1 + \ 113 | min(cache[row-1][column], cache[row] 114 | [column-1], cache[row-1][column-1]) 115 | 116 | max_size = max(max_size, cache[row][column]) 117 | 118 | return max_size 119 | 120 | 121 | class TestSubMatrix(unittest.TestCase): 122 | 123 | def setUp(self): 124 | self.testcases = [([[True]], 1), ([[False]], 0), ([[True, True, True, False], [False, True, True, True], [ 125 | True, True, True, True]], 2), ([[True, True, True, True], [False, True, True, True], [True, True, True, True]], 3)] 126 | 127 | def test_naive_making_change(self): 128 | for value, expected in self.testcases: 129 | with self.subTest(value=value): 130 | self.assertEqual( 131 | SquareSubmatrix.naive_square_submatrix(value), expected) 132 | 133 | def test_top_down_square_submatrix(self): 134 | for value, expected in self.testcases: 135 | with self.subTest(value=value): 136 | self.assertEqual( 137 | SquareSubmatrix.top_down_square_submatrix(value), expected) 138 | 139 | def test_bottom_up_square_submatrix(self): 140 | for value, expected in self.testcases: 141 | with self.subTest(value=value): 142 | self.assertEqual( 143 | SquareSubmatrix.bottom_up_square_submatrix(value), expected) 144 | 145 | 146 | if __name__ == '__main__': 147 | unittest.main() 148 | -------------------------------------------------------------------------------- /python/TargetSum.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # Copyright (c) 2018 Nikolay Derkach 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | 24 | import unittest 25 | 26 | 27 | class TargetSum(object): 28 | 29 | def naive_target_sum(nums, T): 30 | """ 31 | Naive brute force solution. Find every possible combination 32 | """ 33 | return TargetSum.__naive_target_sum(nums, T, 0, 0) 34 | 35 | def __naive_target_sum(nums, T, i, cur_sum): 36 | # When we've gone through every item, see if we've reached our target sum 37 | if i == len(nums): 38 | return int(cur_sum == T) 39 | 40 | # Combine the number of possibilities by adding and subtracting the 41 | # current value 42 | return TargetSum.__naive_target_sum(nums, T, i+1, cur_sum + nums[i]) + TargetSum.__naive_target_sum(nums, T, i+1, cur_sum - nums[i]) 43 | 44 | def top_down_target_sum(nums, T): 45 | """ 46 | Top down dynamic programming solution. Like with 0-1 Knapsack, we use a 47 | hash map to save on space 48 | 49 | Map: i -> sum -> value 50 | """ 51 | 52 | cache = {} 53 | return TargetSum.__top_down_target_sum(nums, T, 0, 0, cache) 54 | 55 | def __top_down_target_sum(nums, T, i, cur_sum, cache): 56 | 57 | if i == len(nums): 58 | return int(cur_sum == T) 59 | 60 | # Check the cache and return value if we get a hit 61 | if i not in cache: 62 | cache[i] = {} 63 | 64 | cached = cache[i].get(cur_sum) 65 | if cached: 66 | return cached 67 | 68 | # If we didn't hit in the cache, compute the value and store to cache 69 | return_value = TargetSum.__top_down_target_sum( 70 | nums, T, i+1, cur_sum + nums[i], cache) + TargetSum.__top_down_target_sum(nums, T, i+1, cur_sum - nums[i], cache) 71 | 72 | cache[i][cur_sum] = return_value 73 | 74 | return return_value 75 | 76 | def bottom_up_target_sum(nums, T): 77 | """ 78 | Bottom up dynamic programming solution 79 | """ 80 | 81 | # Our cache has to range from -sum(nums) to sum(nums), so we offset 82 | # everything by sum 83 | _sum = sum(nums) 84 | 85 | cache = [[0]*(2*_sum + 1) for _ in range(len(nums)+1)] 86 | 87 | if _sum == 0: 88 | return 0 89 | 90 | # Initialize i=0, T=0 91 | cache[0][_sum] = 1 92 | 93 | # Iterate over the previous row and update the current row 94 | for i in range(1, len(nums)+1): 95 | for j in range(2*_sum + 1): 96 | prev = cache[i-1][j] 97 | if prev != 0: 98 | cache[i][j - nums[i-1]] += prev 99 | cache[i][j + nums[i-1]] += prev 100 | 101 | return cache[len(nums)][_sum + T] 102 | 103 | 104 | class TestKnapsack(unittest.TestCase): 105 | 106 | def setUp(self): 107 | self.testcases = [ 108 | ([], 1, 0), ([1, 1, 1, 1, 1], 3, 5), ([1, 1, 1], 1, 3)] 109 | 110 | def test_naive_target_sum(self): 111 | for nums, value, expected in self.testcases: 112 | with self.subTest(value=(nums, value)): 113 | self.assertEqual( 114 | TargetSum.naive_target_sum(nums, value), expected) 115 | 116 | def test_top_down_target_sum(self): 117 | for nums, value, expected in self.testcases: 118 | with self.subTest(value=(nums, value)): 119 | self.assertEqual( 120 | TargetSum.top_down_target_sum(nums, value), expected) 121 | 122 | def test_bottom_up_target_sum(self): 123 | for nums, value, expected in self.testcases: 124 | with self.subTest(value=(nums, value)): 125 | self.assertEqual( 126 | TargetSum.bottom_up_target_sum(nums, value), expected) 127 | 128 | 129 | if __name__ == '__main__': 130 | unittest.main() 131 | --------------------------------------------------------------------------------