{"id":31424,"date":"2019-03-20T10:00:14","date_gmt":"2019-03-20T14:00:14","guid":{"rendered":"https:\/\/simpleprogrammer.com\/?p=31424"},"modified":"2019-03-22T18:46:20","modified_gmt":"2019-03-22T22:46:20","slug":"guide-dynamic-programming","status":"publish","type":"post","link":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/","title":{"rendered":"The Ultimate Guide to Dynamic Programming"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-31459 alignright\" src=\"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/The-Ultimate-Guide-to-Dynamic-Programming-square.png\" alt=\"\" width=\"280\" height=\"280\" \/>Dynamic programming.<\/p>\n<p>Did you feel a little shiver when you read that?<\/p>\n<p>Imagine it again with those spooky Goosebumps letters.<\/p>\n<p><strong>Dynamic programming.<\/strong><\/p>\n<p>When I talk to students of mine over at <a href=\"http:\/\/dynamicprogrammingbook.com\" target=\"_blank\" rel=\"noopener noreferrer\">Byte by Byte<\/a>, nothing quite strikes fear into their hearts like dynamic programming.<\/p>\n<p>And I can totally understand why. Dynamic programming (DP) is as hard as it is counterintuitive. Most of us learn by looking for patterns among different problems. But with dynamic programming, it can be really hard to actually find the similarities.<\/p>\n<p>Even though the problems all use the same technique, they look completely different.<\/p>\n<p>However, there is a way to understand dynamic programming problems and solve them with ease. And in this post I\u2019m going to show you how to do just that.<\/p>\n<h2>What Is Dynamic Programming?<\/h2>\n<p>Before we get into all the details of how to solve dynamic programming problems, it\u2019s key that we answer the most fundamental question:<\/p>\n<p>What is dynamic programming?<\/p>\n<p>Simply put, dynamic programming is an optimization technique that we can use to solve problems where the same work is being repeated over and over. You know how a web server may use caching? Dynamic programming is basically that.<\/p>\n<p>However, dynamic programming doesn\u2019t work for every problem. There are a lot of cases in which dynamic programming simply won\u2019t help us improve the runtime of a problem at all. If we aren\u2019t doing repeated work, then no amount of caching will make any difference.<\/p>\n<p>To determine whether we can optimize a problem using dynamic programming, we can look at both formal criteria of DP problems. I\u2019ll also give you a shortcut in a second that will make these problems much quicker to identify.<\/p>\n<p>Let\u2019s start with the formal definition.<\/p>\n<p>A problem can be optimized using dynamic programming if it:<\/p>\n<ol>\n<li>has an optimal substructure.<\/li>\n<li>has overlapping subproblems.<\/li>\n<\/ol>\n<p>If a problem meets those two criteria, then we know for a fact that it can be optimized using dynamic programming.<\/p>\n<h3>Optimal Substructure<\/h3>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Optimal_substructure\" target=\"_blank\" rel=\"noopener noreferrer\">Optimal substructure<\/a> is a core property not just of dynamic programming problems but also of recursion in general. If a problem can be solved recursively, chances are it has an optimal substructure.<\/p>\n<p>Optimal substructure simply means that you can find the optimal solution to a problem by considering the optimal solution to its subproblems.<\/p>\n<p>For example, if we are looking for the shortest path in a graph, knowing the partial path to the end (the bold squiggly line in the image below), we can compute the shortest path from the start to the end, without knowing any details about the squiggly path.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-31427\" src=\"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/dynamic-programming-1.png\" alt=\"\" width=\"1600\" height=\"858\" \/><\/p>\n<p>What might be an example of a problem without optimal substructure?<\/p>\n<p>Consider finding the cheapest flight between two airports. According to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Optimal_substructure\" target=\"_blank\" rel=\"noopener noreferrer\">Wikipedia<\/a>:<\/p>\n<p>\u201cUsing online flight search, we will frequently find that the cheapest flight from airport A to airport B involves a single connection through airport C, but the cheapest flight from airport A to airport C involves a connection through some other airport D.\u201d<\/p>\n<p>While there is some nuance here, we can generally assume that any problem that we solve recursively will have an optimal substructure.<\/p>\n<h3>Overlapping Subproblems<\/h3>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Overlapping_subproblems\" target=\"_blank\" rel=\"noopener noreferrer\">Overlapping subproblems<\/a> is the second key property that our problem must have to allow us to optimize using dynamic programming. Simply put, having overlapping subproblems means we are computing the same problem more than once.<\/p>\n<p>Imagine you have a server that caches images. If the same image gets requested over and over again, you\u2019ll save a ton of time. However, if no one ever requests the same image more than once, what was the benefit of caching them?<\/p>\n<p>This is exactly what happens here. If we don\u2019t have overlapping subproblems, there is nothing to stop us from caching values. It just won\u2019t actually improve our runtime at all. All it will do is create more work for us.<\/p>\n<p>For an example of overlapping subproblems, consider the Fibonacci problem. Here is a tree of all the recursive calls required to compute the fifth Fibonacci number:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-31428\" src=\"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/dynamic-programming-2.png\" alt=\"\" width=\"1600\" height=\"1127\" \/><\/p>\n<p>Notice how we see repeated values in the tree. The number 3 is repeated twice, 2 is repeated three times, and 1 is repeated five times. Each of those repeats is an overlapping subproblem. There is no need for us to compute those subproblems multiple times because the value won\u2019t change. If we cache it, we can save ourselves a lot of work.<\/p>\n<h3>When Should I Use Dynamic Programming?<\/h3>\n<p>To be absolutely certain that we can solve a problem using <a href=\"https:\/\/simpleprogrammer.com\/get\/dynamicprogramming\">dynamic programming<\/a>, it is critical that we test for optimal substructure and overlapping subproblems. Without those, we can\u2019t use dynamic programming.<\/p>\n<p>However, we can use heuristics to guess pretty accurately whether or not we should even consider using DP. This quick question can save us a ton of time.<\/p>\n<p>All we have to ask is: Can this problem be solved by solving a combination problem?<\/p>\n<p><iframe loading=\"lazy\" src=\"https:\/\/www.youtube.com\/embed\/qyu4q3-glLU\" width=\"560\" height=\"315\" frameborder=\"0\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<p>Consider a few examples:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.byte-by-byte.com\/smallestchange\/\" target=\"_blank\" rel=\"noopener noreferrer\">Find the smallest number of coins required to make a specific amount of change<\/a>. Look at all combinations of coins that add up to an amount, and count the fewest number.<\/li>\n<li><a href=\"https:\/\/www.byte-by-byte.com\/01knapsack\/\" target=\"_blank\" rel=\"noopener noreferrer\">Find the most value of items that can fit in your knapsack<\/a>. Find all combinations of items and determine the highest value combination.<\/li>\n<li><a href=\"https:\/\/leetcode.com\/problems\/climbing-stairs\/\" target=\"_blank\" rel=\"noopener noreferrer\">Find the number of different paths to the top of a staircase<\/a>. Enumerate all the combinations of steps.<\/li>\n<\/ul>\n<p>While this heuristic doesn\u2019t account for all dynamic programming problems, it does give you a quick way to gut-check a problem and decide whether you want to go deeper.<\/p>\n<h2>Solve Any DP Problem Using the FAST Method<\/h2>\n<p>After seeing many of my students from <a href=\"http:\/\/byte-by-byte.com\" target=\"_blank\" rel=\"noopener noreferrer\">Byte by Byte<\/a> struggling so much with dynamic programming, I realized we had to do something. There had to be a system for these students to follow that would help them solve these problems consistently and without stress.<\/p>\n<p>It was this mission that gave rise to <a href=\"https:\/\/www.byte-by-byte.com\/dpbook\/\" target=\"_blank\" rel=\"noopener noreferrer\">The FAST Method<\/a>.<\/p>\n<p>The FAST Method is a technique that has been pioneered and tested over the last several years. As I write this, more than 8,000 of our students have downloaded our <a href=\"https:\/\/www.byte-by-byte.com\/dpbook\/\" target=\"_blank\" rel=\"noopener noreferrer\">free e-book<\/a> and learned to master dynamic programming using The FAST Method.<\/p>\n<p>So how does it work?<\/p>\n<p>FAST is an acronym that stands for <strong>F<\/strong>ind the first solution, <strong>A<\/strong>nalyze the solution, identify the <strong>S<\/strong>ubproblems, and <strong>T<\/strong>urn around the solution. Let\u2019s break down each of these steps.<\/p>\n<h3>Find the First Solution<\/h3>\n<p>The first step to solving any dynamic programming problem using The FAST Method is to find the initial brute force recursive solution.<\/p>\n<p>Your goal with Step One is to solve the problem without concern for efficiency. We just want to get a solution down on the whiteboard. This gives us a starting point (I\u2019ve discussed this in much more detail <a href=\"https:\/\/www.byte-by-byte.com\/brute-force\/\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a>).<\/p>\n<p>There are a couple of restrictions on how this brute force solution should look:<\/p>\n<ul>\n<li><strong>Each recursive call must be self-contained<\/strong>. If you are storing your result by updating some global variable, then it will be impossible for us to use any sort of caching effectively. We want to have a result that is completely dependent on the inputs of the function and not affected by any outside factors.<\/li>\n<li><strong>Remove unnecessary variables.<\/strong> The fewer variables you pass into your recursive function, the better. We will be saving our cached values based on the inputs to the function, so it will be a pain if we have extraneous variables.<\/li>\n<\/ul>\n<p>Let\u2019s consider two examples here. We\u2019ll use these examples to demonstrate each step along the way.<\/p>\n<p><strong>Example 1: <\/strong><\/p>\n<p>The first problem we\u2019re going to look at is the Fibonacci problem. In this problem, we want to simply identify the n-th Fibonacci number. Recursively we can do that as follows:<\/p>\n<pre><code>\n\/\/ Compute the nth Fibonacci number\n\/\/ We assume that n &gt;= 0 and that int is sufficient to hold the result\nint fib(int n) {\nif (n == 0) return 0;\nif (n == 1) return 1;\nreturn fib(n-1) + fib(n-2);\n}\n<\/code><\/pre>\n<p>It is important to notice here how each result of <code>fib(n)<\/code> is 100 percent dependent on the value of \u201cn.\u201d We have to be careful to write our function in this way. For example, while the following code works, it would NOT allow us to do DP.<\/p>\n<pre><code>\n\/\/ Compute the nth Fibonacci number\n\/\/ We assume that n &gt;= 0 and that int is sufficient to hold the result\nint fib(int n) {\nint result = 0;\nfibInner(n, &amp;result);\nreturn result;\n}\nvoid fibInner(int n, int *result) {\n*result += n;\nif (n == 0 || n == 1) return;\nfibInner(n-1, result);\nfibInner(n-2, result);\n}\n<\/code><\/pre>\n<p>While this may seem like a toy example, it is really important to understand the difference here. This second version of the function is reliant on <code>result<\/code> to compute the result of the function and <code>result<\/code> is scoped outside of the <code>fibInner()<\/code> function.<\/p>\n<p><a href=\"https:\/\/www.byte-by-byte.com\/recursion\/\" target=\"_blank\" rel=\"noopener noreferrer\">You can learn more about the difference here.<\/a><\/p>\n<p><strong>Example 2: <\/strong><\/p>\n<p>The second problem that we\u2019ll look at is one of the most popular dynamic programming problems: 0-1 Knapsack Problem. For this problem, we are given a list of items that have weights and values, as well as a max allowable weight. We want to determine the maximum value that we can get without exceeding the maximum weight.<\/p>\n<p>Here is our brute force recursive code:<\/p>\n<pre><code>\npublic class Item {\nint weight;\nint value;\n}\nclass Item {\nint weight;\nint value;\n}\n\/\/ Brute force solution\nint bruteForceKnapsack(Item[] items, int W) {\nreturn bruteForceKnapsack(items, W, 0);\n}\n\/\/ Overloaded recursive function\nint bruteForceKnapsack(Item[] items, int W, int i) {\n\/\/ If we've gone through all the items, return\nif (i == items.length) return 0;\n\/\/ If the item is too big to fill the remaining space, skip it\nif (W - items[i].weight &lt; 0) return bruteForceKnapsack(items, W, i+1);<\/code> <code>\n\/\/ Find the maximum of including and not including the current item\nreturn Math.max(bruteForceKnapsack(items, W - items[i].weight, i+1)\n+ items[i].value,\nbruteForceKnapsack(items, W, i+1));\n}<\/code><\/pre>\n<p>With these brute force solutions, we can move on to the next step of The FAST Method.<\/p>\n<p><strong>Note:<\/strong> I\u2019ve found that many people find this step difficult. It is way too large a topic to cover here, so if you struggle with recursion, I recommend checking out <a href=\"https:\/\/www.byte-by-byte.com\/recursion\/\" target=\"_blank\" rel=\"noopener noreferrer\">this monster post on Byte by Byte<\/a>.<\/p>\n<h3>Analyze the First Solution<\/h3>\n<p>Now that we have our brute force solution, the next step in The FAST Method is to analyze the solution. In this step, we are looking at the runtime of our solution to see if it is worth trying to use dynamic programming and then considering whether we can use it for this problem at all.<\/p>\n<p>We will start with a look at the time and space complexity of our problem and then jump right into an analysis of whether we have optimal substructure and overlapping subproblems. Remember that those are required for us to be able to use dynamic programming.<\/p>\n<p>Let\u2019s go back to our Fibonacci example.<\/p>\n<p><strong>Example 1 (Fibonacci):<\/strong><\/p>\n<p>For this problem, our code was nice and simple, but unfortunately our time complexity sucks. The easiest way to get a handle on what is going on in your code is to sketch out the recursive tree. Here\u2019s the tree for <code>fib(4)<\/code>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-31429\" src=\"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/Fib-4.png\" alt=\"\" width=\"512\" height=\"253\" \/><\/p>\n<p>What we immediately notice here is that we essentially get a tree of height <code>n<\/code>. Yes, some of the branches are a bit shorter, but our Big Oh complexity is an upper bound. Therefore, to compute the time complexity, we can simply estimate the number of nodes in the tree.<\/p>\n<p>For any tree, we can estimate the number of nodes as <code>branching_factorheight<\/code>, where the branching factor is the maximum number of children that any node in the tree has. That gives us a pretty terrible runtime of <code>O(2n)<\/code>.<\/p>\n<p>So it would be nice if we could optimize this code, and if we have optimal substructure and overlapping subproblems, we could do just that.<\/p>\n<p>In this case, we have a recursive solution that pretty much guarantees that we have an optimal substructure. We are literally solving the problem by solving some of its subproblems.<\/p>\n<p>We also can see clearly from the tree diagram that we have overlapping subproblems. Notice <code>fib(2)<\/code> getting called two separate times? That\u2019s an overlapping subproblem. If we drew a bigger tree, we would find even more overlapping subproblems.<\/p>\n<p>Given that we have found this solution to have an exponential runtime and it meets the requirements for dynamic programming, this problem is clearly a prime candidate for us to optimize.<\/p>\n<p><strong>Example 2 (0-1 Knapsack):<\/strong><\/p>\n<p>The code for this problem was a little bit more complicated, so drawing it out becomes even more important. When we sketch out an example, it gives us much more clarity on what is happening (<a href=\"https:\/\/www.byte-by-byte.com\/recursion\/#section3\" target=\"_blank\" rel=\"noopener noreferrer\">see my process for sketching out solutions<\/a>).<\/p>\n<p>Here\u2019s what our tree might look like for the following inputs:<\/p>\n<pre><code>\nItems = {(w:2, v:6), (w:2, v:10), (w:3, v:12)}\n\nmaxWeight = 5\n<\/code><\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-31425\" src=\"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/Knapsack-1.png\" alt=\"\" width=\"1600\" height=\"643\" \/><\/p>\n<p><em>Note the two values passed into the function in this diagram are the maxWeight and the current index in our items list.<\/em><\/p>\n<p>So with our tree sketched out, let\u2019s start with the time complexity. Similar to our Fibonacci problem, we see that we have a branching tree of recursive calls where our branching factor is 2. This gives us a time complexity of <code>O(2n)<\/code>.<\/p>\n<p>Do we have optimal substructure? Yep. Again, the recursion basically tells us all we need to know on that count.<\/p>\n<p>And overlapping subproblems? Since we\u2019ve sketched it out, we can see that <code>knapsack(3, 2)<\/code> is getting called twice, which is a clearly overlapping subproblem.<\/p>\n<p>This also looks like a good candidate for DP.<\/p>\n<h3>Identify the Subproblems<\/h3>\n<p>The third step of The FAST Method is to identify the subproblems that we are solving. This is where we really get into the meat of optimizing our code.<\/p>\n<p>We are going to start by defining in plain English what exactly our subproblem is. I\u2019m always shocked at how many people can write the recursive code but don\u2019t really understand what their code is doing. Understanding is critical.<\/p>\n<p>Once we understand the subproblems, we can implement a cache that will <strong>memoize<\/strong> the results of our subproblems, giving us a <strong>top-down<\/strong> dynamic programming solution.<\/p>\n<p>Memoization is simply the strategy of caching the results. We can use an array or map to save the values that we\u2019ve already computed to easily look them up later.<\/p>\n<p>We call this a top-down dynamic programming solution because we are solving it recursively. Essentially we are starting at the \u201ctop\u201d and recursively breaking the problem into smaller and smaller chunks. This is in contrast to <strong>bottom-up<\/strong>, or <strong>tabular<\/strong>, dynamic programming, which we will see in the last step of The FAST Method.<\/p>\n<p><strong>Example 1 (Fibonacci):<\/strong><\/p>\n<p>So what is our subproblem here? This problem is quite easy to understand because <code>fib(n)<\/code> is simply the nth Fibonacci number. Since our result is only dependent on a single variable, <code>n<\/code>, it is easy for us to memoize based on that single variable.<\/p>\n<p>Consider the code below. By adding a simple array, we can memoize our results. Notice the differences between this code and our code above:<\/p>\n<pre><code>\/\/ Top-down dynamic solution\nint topDownFib(int n) {\nint[] dp = new int[n+1];\nreturn topDownFib(n, dp);\n}\n\/\/ Overloaded private method\nint topDownFib(int n, int[] dp) {\nif (n == 0 || n == 1) return n;\n\/\/ If value is not set in cache, compute it\nif (dp[n] == 0) {\ndp[n] = topDownFib(n-1, dp) + topDownFib(n-2, dp);\n}\nreturn dp[n];\n} \n<\/code><\/pre>\n<p>See how little we actually need to change? Once we understand our subproblem, we know exactly what value we need to cache.<\/p>\n<p>Now that we have our top-down solution, we do also want to look at the complexity. In this case, our code has been reduced to <code>O(n)<\/code> time complexity. We can pretty easily see this because each value in our <code>dp<\/code> array is computed once and referenced some constant number of times after that. This is much better than our previous exponential solution.<\/p>\n<p><strong>Example 2 (0-1 Knapsack):<\/strong><\/p>\n<p>This problem starts to demonstrate the power of truly understanding the subproblems that we are solving. Specifically, not only does <code>knapsack()<\/code> take in a weight, it also takes in an index as an argument. So if you call <code>knapsack(4, 2)<\/code> what does that actually mean? What is the result that we expect? Well, if you look at the code, we can formulate a plain English definition of the function:<\/p>\n<p>Here, \u201c<code>knapsack(maxWeight, index)<\/code> returns the maximum value that we can generate under a current weight only considering the items from index to the end of the list of items.\u201d<\/p>\n<p>With this definition, it makes it easy for us to rewrite our function to cache the results (and in the next section, these definitions will become invaluable):<\/p>\n<pre><code>\n\/\/ Top-down dynamic solution\nint topDownKnapsackArray(Item[] items, int W) {\nint[][] dp = new int[items.length][W + 1];\nfor (int i = 0; i &lt; dp.length; i++) {\nfor (int j = 0; j &lt; dp[0].length; j++) {\ndp[i][j] = -1;\n}\n}\nreturn topDownKnapsackArray(items, W, 0, dp);\n}\n\/\/ Overloaded recursive function\nint topDownKnapsackArray(Item[] items, int W, int i, int[][] dp) {\nif (i == items.length) return 0;\nif (dp[i][W] == -1) {\ndp[i][W] = topDownKnapsackArray(items, W, i+1, dp);\nif (W - items[i].weight &gt;= 0) {\nint include = topDownKnapsackArray(items, W-items[i].weight, i+1, dp) + items[i].value;\ndp[i][W] = Math.max(dp[i][W], include);\n}\n}\nreturn dp[i][W];\n\n}\n\n<\/code><\/pre>\n<p>Again, we can see that very little change to our original code is required. All we are doing is adding a cache that we check before computing any function. If the value in the cache has been set, then we can return that value without recomputing it.<\/p>\n<p>In terms of the time complexity here, we can turn to the size of our cache. Each value in the cache gets computed at most once, giving us a complexity of <code>O(n*W)<\/code>.<\/p>\n<h3>Turn Around the Solution<\/h3>\n<p>The final step of The FAST Method is to take our top-down solution and \u201cturn it around\u201d into a bottom-up solution. This is an optional step, since the top-down and bottom-up solutions will be equivalent in terms of their complexity. However, many prefer bottom-up due to the fact that iterative code tends to run faster than recursive code.<\/p>\n<p>With this step, we are essentially going to invert our top-down solution. Instead of starting with the goal and breaking it down into smaller subproblems, we will start with the smallest version of the subproblem and then build up larger and larger subproblems until we reach our target. This is where the definition from the previous step will come in handy.<\/p>\n<p><strong>Example 1 (Fibonacci):<\/strong><\/p>\n<p>To start, let\u2019s recall our subproblem: <code>fib(n)<\/code> is the nth Fibonacci number.<\/p>\n<p>Remember that we\u2019re going to want to compute the smallest version of our subproblem first. That would be our base cases, or in this case, <code>n = 0<\/code> and <code>n = 1<\/code>.<\/p>\n<p>Once we have that, we can compute the next biggest subproblem. To get <code>fib(2)<\/code>, we just look at the subproblems we\u2019ve already computed. Once that\u2019s computed we can compute <code>fib(3)<\/code> and so on.<\/p>\n<p>So how do we write the code for this? Well, our cache is going to look identical to how it did in the previous step; we\u2019re just going to fill it in from the smallest subproblems to the largest, which we can do iteratively.<\/p>\n<pre><code>\n\/\/ Bottom-up dynamic solution\nint bottomUpFib(int n) {\nif (n == 0) return 0;\n\/\/ Initialize cache\nint[] dp = new int[n+1];\ndp[1] = 1;\n\u00a0\n\/\/ Fill cache iteratively\nfor (int i = 2; i &lt;= n; i++) {\ndp[i] = dp[i-1] + dp[i-2];\n}\n\u00a0\n\nreturn dp[n];\n\n}\n\n<\/code><\/pre>\n<p>Easy peasy lemon squeezy.<\/p>\n<p>Another nice perk of this bottom-up solution is that it is super easy to compute the time complexity. Unlike recursion, with basic iterative code it\u2019s easy to see what\u2019s going on.<\/p>\n<p>And that\u2019s all there is to it. One note with this problem (and some other DP problems) is that we can <a href=\"https:\/\/www.byte-by-byte.com\/01knapsack\/\" target=\"_blank\" rel=\"noopener noreferrer\">further optimize the space complexity<\/a>, but that is outside the scope of this post.<\/p>\n<p><strong>Example 2 (0-1 Knapsack):<\/strong><\/p>\n<p>As is becoming a bit of a trend, this problem is much more difficult. However, you now have all the tools you need to solve the Knapsack problem bottom-up.<\/p>\n<p>Recall our subproblem definition: \u201c<code>knapsack(maxWeight, index)<\/code> returns the maximum value that we can generate under a current weight only considering the items from index to the end of the list of items.\u201d<\/p>\n<p>To make things a little easier for our bottom-up purposes, we can invert the definition so that rather than looking from the index to the end of the array, our subproblem can solve for the array up to, but not including, the index.<\/p>\n<p>With this, we can start to fill in our base cases. We\u2019ll start by initializing our <code>dp<\/code> array. Whenever the max weight is 0, <code>knapsack(0, index)<\/code> has to be 0. Referring back to our subproblem definition, that makes sense. If the weight is 0, then we can\u2019t include any items, and so the value must be 0.<\/p>\n<p>The same holds if index is 0. Since we define our subproblem as the value for all items up to, but not including, the index, if index is 0 we are also including 0 items, which has 0 value.<\/p>\n<p>From there, we can iteratively compute larger subproblems, ultimately reaching our target:<\/p>\n<pre><code>\n\/\/ Bottom-up dynamic solution.\nint bottomUpKnapsack(Item[] items, int W) {\nif (items.length == 0 || W == 0) return 0;\n\/\/ Initialize cache\nint[][] dp = new int[items.length + 1][W + 1];\n\/\/ For each item and weight, compute the max value of the items up to\n\/\/ that item that doesn't go over W weight\nfor (int i = 1; i &lt; dp.length; i++) {\nfor (int j = 0; j &lt; dp[0].length; j++) {\nif (items[i-1].weight &gt; j) {\ndp[i][j] = dp[i-1][j];\n} else {\nint include = dp[i-1][j-items[i-1].weight] + items[i-1].value;\ndp[i][j] = Math.max(dp[i-1][j], include);\n}\n\n}\n\n}\n\nreturn dp[items.length][W];\n\n}\n\n<\/code><\/pre>\n<p>Again, once we solve our solution bottom-up, the time complexity becomes very easy because we have a simple nested <code>for<\/code> loop.<\/p>\n<h2>Dynamic Programming Doesn\u2019t Have to Be Hard!<\/h2>\n<p>While dynamic programming seems like a scary and counterintuitive \u00a0topic, it doesn\u2019t have to be. By applying structure to your solutions, such as with The FAST Method, it is possible to solve any of these problems in a systematic way.<\/p>\n<p>Interviewers love to test candidates on dynamic programming because it is perceived as such a difficult topic, but there is no need to be nervous. Follow the steps and you\u2019ll do great.<\/p>\n<p>If you want to learn more about The FAST Method, check out my free e-book, <em><a href=\"https:\/\/www.byte-by-byte.com\/dpbook\/\" target=\"_blank\" rel=\"noopener noreferrer\">Dynamic Programming for Interviews<\/a>.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dynamic programming. Did you feel a little shiver when you read that? Imagine it again with those spooky Goosebumps letters. Dynamic programming. When I talk to students of mine over at Byte by Byte, nothing quite strikes fear into their hearts like dynamic programming. And I can totally understand why. Dynamic programming (DP) is as&#8230;<\/p>\n","protected":false},"author":1114,"featured_media":31460,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"categories":[415,31679,162229803,162229840,162229253],"tags":[],"class_list":["post-31424","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-guest-post","category-process-improvement","category-programming","category-skills","category-technical"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>The Ultimate Guide to Dynamic Programming - Simple Programmer<\/title>\n<meta name=\"description\" content=\"Lots of people find dynamic programming to be difficult, but it doesn\u2019t have to be. Using The FAST Method, you can solve any problem with ease.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"The Ultimate Guide to Dynamic Programming - Simple Programmer\" \/>\n<meta property=\"og:description\" content=\"Lots of people find dynamic programming to be difficult, but it doesn\u2019t have to be. Using The FAST Method, you can solve any problem with ease.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/\" \/>\n<meta property=\"og:site_name\" content=\"Simple Programmer\" \/>\n<meta property=\"article:published_time\" content=\"2019-03-20T14:00:14+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-03-22T22:46:20+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/The-Ultimate-Guide-to-Dynamic-Programming.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1280\" \/>\n\t<meta property=\"og:image:height\" content=\"720\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"19 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"The Ultimate Guide to Dynamic Programming\",\"datePublished\":\"2019-03-20T14:00:14+00:00\",\"dateModified\":\"2019-03-22T22:46:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/\"},\"wordCount\":3217,\"commentCount\":0,\"image\":{\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/simpleprogrammer.com\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/The-Ultimate-Guide-to-Dynamic-Programming.png\",\"articleSection\":[\"Guest Post\",\"Process Improvement\",\"Programming\",\"Skills\",\"Technical\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/\",\"url\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/\",\"name\":\"The Ultimate Guide to Dynamic Programming - Simple Programmer\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/simpleprogrammer.com\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/The-Ultimate-Guide-to-Dynamic-Programming.png\",\"datePublished\":\"2019-03-20T14:00:14+00:00\",\"dateModified\":\"2019-03-22T22:46:20+00:00\",\"author\":{\"@id\":\"\"},\"description\":\"Lots of people find dynamic programming to be difficult, but it doesn\u2019t have to be. Using The FAST Method, you can solve any problem with ease.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/#primaryimage\",\"url\":\"https:\\\/\\\/simpleprogrammer.com\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/The-Ultimate-Guide-to-Dynamic-Programming.png\",\"contentUrl\":\"https:\\\/\\\/simpleprogrammer.com\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/The-Ultimate-Guide-to-Dynamic-Programming.png\",\"width\":1280,\"height\":720},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/guide-dynamic-programming\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/simpleprogrammer.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"The Ultimate Guide to Dynamic Programming\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/simpleprogrammer.com\\\/#website\",\"url\":\"https:\\\/\\\/simpleprogrammer.com\\\/\",\"name\":\"Simple Programmer\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/simpleprogrammer.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"\",\"url\":\"https:\\\/\\\/simpleprogrammer.com\\\/author\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"The Ultimate Guide to Dynamic Programming - Simple Programmer","description":"Lots of people find dynamic programming to be difficult, but it doesn\u2019t have to be. Using The FAST Method, you can solve any problem with ease.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/","og_locale":"en_US","og_type":"article","og_title":"The Ultimate Guide to Dynamic Programming - Simple Programmer","og_description":"Lots of people find dynamic programming to be difficult, but it doesn\u2019t have to be. Using The FAST Method, you can solve any problem with ease.","og_url":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/","og_site_name":"Simple Programmer","article_published_time":"2019-03-20T14:00:14+00:00","article_modified_time":"2019-03-22T22:46:20+00:00","og_image":[{"width":1280,"height":720,"url":"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/The-Ultimate-Guide-to-Dynamic-Programming.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_misc":{"Written by":"","Est. reading time":"19 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/#article","isPartOf":{"@id":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/"},"author":{"name":"","@id":""},"headline":"The Ultimate Guide to Dynamic Programming","datePublished":"2019-03-20T14:00:14+00:00","dateModified":"2019-03-22T22:46:20+00:00","mainEntityOfPage":{"@id":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/"},"wordCount":3217,"commentCount":0,"image":{"@id":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/#primaryimage"},"thumbnailUrl":"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/The-Ultimate-Guide-to-Dynamic-Programming.png","articleSection":["Guest Post","Process Improvement","Programming","Skills","Technical"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/","url":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/","name":"The Ultimate Guide to Dynamic Programming - Simple Programmer","isPartOf":{"@id":"https:\/\/simpleprogrammer.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/#primaryimage"},"image":{"@id":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/#primaryimage"},"thumbnailUrl":"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/The-Ultimate-Guide-to-Dynamic-Programming.png","datePublished":"2019-03-20T14:00:14+00:00","dateModified":"2019-03-22T22:46:20+00:00","author":{"@id":""},"description":"Lots of people find dynamic programming to be difficult, but it doesn\u2019t have to be. Using The FAST Method, you can solve any problem with ease.","breadcrumb":{"@id":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/#primaryimage","url":"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/The-Ultimate-Guide-to-Dynamic-Programming.png","contentUrl":"https:\/\/simpleprogrammer.com\/wp-content\/uploads\/2019\/03\/The-Ultimate-Guide-to-Dynamic-Programming.png","width":1280,"height":720},{"@type":"BreadcrumbList","@id":"https:\/\/simpleprogrammer.com\/guide-dynamic-programming\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/simpleprogrammer.com\/"},{"@type":"ListItem","position":2,"name":"The Ultimate Guide to Dynamic Programming"}]},{"@type":"WebSite","@id":"https:\/\/simpleprogrammer.com\/#website","url":"https:\/\/simpleprogrammer.com\/","name":"Simple Programmer","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/simpleprogrammer.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"","url":"https:\/\/simpleprogrammer.com\/author\/"}]}},"_links":{"self":[{"href":"https:\/\/simpleprogrammer.com\/wp-json\/wp\/v2\/posts\/31424","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/simpleprogrammer.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/simpleprogrammer.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/simpleprogrammer.com\/wp-json\/wp\/v2\/users\/1114"}],"replies":[{"embeddable":true,"href":"https:\/\/simpleprogrammer.com\/wp-json\/wp\/v2\/comments?post=31424"}],"version-history":[{"count":0,"href":"https:\/\/simpleprogrammer.com\/wp-json\/wp\/v2\/posts\/31424\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/simpleprogrammer.com\/wp-json\/wp\/v2\/media\/31460"}],"wp:attachment":[{"href":"https:\/\/simpleprogrammer.com\/wp-json\/wp\/v2\/media?parent=31424"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/simpleprogrammer.com\/wp-json\/wp\/v2\/categories?post=31424"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/simpleprogrammer.com\/wp-json\/wp\/v2\/tags?post=31424"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}