General algorithm collation: Dynamic programming

Source: Internet
Author: User

What is dynamic planning

Dynamic planning should not be called 一种算法 , but should be called 一类算法 or said to be 一种思想 . It 二分查找 is different from this algorithm, binary lookup we can express it in code, and the idea of solving all problems is almost the same. and dynamic planning can not be expressed in code, each solution of the problem may be written in code is not the same, so only to see the definition of dynamic programming is difficult to understand, need to take a number of topics to really understand.

The characteristic of dynamic programming is to decompose a big problem into several small problems, and each small problem can be calculated by the small problem in front. Usually these small problems are in fact a specific state. If a problem can be solved with dynamic planning, the following features are required:

    1. The problem can be decomposed into dp[0], dp[1], … , dp[n] a state, the final answer is one of the states, generally dp[n] . (or dp[n][m] and more dimensions, we only take one dimension here)
    2. Which dp[i] can be dp[0] ~ dp[i-1] calculated by or ' dp[i+1]~dp[n '.

If you look at the definition only, you will find that DP must be solved by the method of division and administration. Then why do we need to solve the DP algorithm, because many times the division algorithm will have a large number of repeated calculations, and dynamic planning due to record the state, time complexity will be greatly reduced. Many times the complexity of time can be O(2^n) reduced to O(n^2) . If the divide-and-conquer method adds a cache to the computed nodes, then his time complexity is the same as DP, in fact DP = DC + Cache .

From the above two features can be seen, DP problem has two difficulties:

    1. If defined dp[i] , that is, how to split the problem, define the state
    2. How to calculate dp[i] , that is, how to calculate the next state through the previous state

In addition to these two difficulties, one thing is to deal with the initial values and possible boundary overflow problems.

In addition, if the topic is required to give all solutions, certainly not DP. DP is generally the optimal solution or the number of solutions.

Simple, dynamic planning

The simplest kind of dynamic programming, see this topic: https://leetcode.com/problems/triangle/

Here we use an array of the same triangle as the one given in the topic dp to store the state. Solve the two difficult points mentioned above:

    1. What if the problem is split? In fact dp[i][j] , this represents the triangle[i][j] shortest path required to jump to this point.
    2. If calculated dp[i][j] ? DP[I][J] is actually taking the smaller one in the adjacent node of his upper layerdp[i][j] = Math.min(last[j-1], last[j])+t[j]

Then the boundary condition is processed, and the problem can be solved. The code is as follows:

var minimumTotal = function(triangle) {    if(!triangle.length) return 0;    var dp = [triangle[0]];         //注意这里处理的初始值    for(var i=1;i<triangle.length;i++) {        var last = dp[i-1];        var row = [];        var t = triangle[i];        for(var j=0;j<t.length;j++) {            if(j === 0) row.push(last[0]+t[0]);                 //处理左边界            else if(j === t.length-1) row.push(last[last.length-1]+t[t.length-1]);                 //处理右边界            else row.push(Math.min(last[j-1], last[j])+t[j]);        }        dp.push(row);    }    var r = dp.pop(), min = r[0];    for(i=0;i<r.length;i++) {        if(r[i]<min) min = r[i];    }    return min;};

Analyze the time complexity, if the number of n nodes is expressed, then the node number is approximately O(n^2) , in this algorithm in fact we have the entire triangle has been traversed only, so the time complexity is also O(n^2) .

Compared to DC, here we can easily write a DC algorithm, and more simple than DP. Each time the DC algorithm calculates the i,j value of a node, it needs to perform a two-recursive calculation, respectively, i-1,j-1 so that i-1,j each call will produce two new recursion, each layer of the calculation is twice times the previous layer, the time complexity is O(2^n) .

Let's look at one simple question: https://leetcode.com/problems/unique-paths/

Here we use DP[I][J] to indicate the i,j number of all the different paths moved to. The code is as follows:

var uniquePaths = function(m, n) {    var dp = [], i, j;    for(i=0;i<m;i++) {        var row = [];        for(j=0;j<n;j++) {            row.push(0);        }        row[0] = 1;        dp.push(row);    }    for(i=0;i<m;i++) {        for(j=1;j<n;j++) {            if(i === 0) dp[i][j] = dp[i][j-1];            else dp[i][j] = dp[i][j-1] + dp[i-1][j];        }    }    return dp[m-1][n-1];};
The problem of medium difficulty

The above two DP problems are Easy difficult problems, said it Easy is mainly to our definition of the two difficulties are actually easy to solve. That's easy dp[i] to define, and it's easy to figure out dp[i] . Here are a few questions that will be slightly more difficult.

First question, https://leetcode.com/problems/longest-increasing-subsequence/.

Here are some of the more difficult things we can define dp[i] , such as two main ideas:

    1. 0~i the oldest sequence of elements
    2. Oldest sequence ending with an I element

If we use the first definition, we will find that it cannot be dp[0] ~ dp[i-1] calculated dp[i] because it is not possible to know whether the ending value of the preceding oldest sequence is smaller than the I element. So we can only choose a second definition.

That is dp[i] 表示以 i 元素结尾的最长子序列 , so that we can calculate the dp[i], as long as the traverse to find the longest dp[0~i-1] sub-sequence, which is less than the current element, and then the length of their own is dp[i] , the specific solution is as follows:

var lengthOfLIS = function(nums) {    if(nums.length <= 1) return nums.length;    var dp = [], i, result=1;    for(i=0;i<nums.length;i++) {        dp.push(1);    }    for(i=1;i<nums.length;i++) {        for(var j=0;j<i;j++) {            if(nums[j] < nums[i]) dp[i] = Math.max(dp[i], 1+dp[j]);        }        result = Math.max(dp[i], result);    }    return result;};

The time complexity of this procedure is O (n^2), because he is a double loop.

The second question, https://leetcode.com/problems/palindrome-partitioning-ii/

This is a very difficult topic, and we define it dp[i] as the i minimum cut required by the previous element. Note that it 前i个 does not include the current element I. As for why the definition is 前i个 not 0~i 个 , in fact, mainly to deal with the boundary conditions more convenient, defined as 0~i个 can be solved.

Once defined dp[i] , we need a way to calculate its value. A clear idea is: Traversal dp[j=0~i-1] , if the j~i element is a palindrome string, then dp[i] = dp[j]+1, just take a minimum value on the line.

In doing so, a code like this would appear:

for(i=1;i<=s.length;i++) {    for(var j=0;j<i;j++) {        if(isPalindrome(j,i-1)) dp[i] = Math.min(dp[j]+1, dp[i]);    }}

There are already two layers in the loop, that is, the time complexity is at least O(n^2) , so the function of which isPalindrome we can not cycle again, or become O(n^3) , this will definitely time out.
One approach is to calculate all the cases in advance and then save them in an array of ispalindrome[n][n], which requires extra O(n^2) space. If you do this on the Leetcode, the memory overrun error will be reported.

Then you can announce that the idea cannot get a O (n^2) time complexity, and the spatial complexity is the solution of O (n) . This idea is relatively easy to think of, and the implementation is not complicated. It seems that Java can be passed, but with JS is impossible to pass. Put the complete code for this idea:

var minCut = function(s) {    if(s.length <= 1) return 0;    var isPalindrome = caculatePalindrome(s);    var dp = [-1];    for(var i=1;i<=s.length;i++) {    dp.push(i-1);    }    for(i=1;i<=s.length;i++) {        for(var j=0;j<i;j++) {            if(isPalindrome[j][i-1]) dp[i] = Math.min(dp[j]+1, dp[i]);        }    }    return dp[s.length];};var caculatePalindrome = function(s) {    var dp = [];    for(var i=0;i<s.length;i++) {        var row = [];        for(var j=0;j<=i;j++) {            row.push(false);        }        dp.push(row);    }    for(i=0;i<s.length;i++) {        for(j=0;j<=i;j++) {            if(i === j) dp[j][i] = true;            else if(j === i-1) dp[j][i] = (s[i] === s[j]);            else dp[j][i] = (s[i] === s[j] && dp[j+1][i-1]);        }    }    return dp;}

Here we change a train of thought, this idea is very difficult to think. Is that we actually in the second layer of the loop at the same time, we can determine whether it is a palindrome string, do not need an additional judgment. In a specific way, we look directly at the code to illustrate:

var minCut = function(s) {    if(s.length <= 1) return 0;    var dp = [];    for(var i=0;i<=s.length;i++) {        dp.push(i-1);    }    for(i=0;i<=s.length;i++) {        for(var j=0;i-j>=0 && i+j<s.length && s[i-j] === s[i+j]; j++) {            dp[i+j+1] = Math.min(dp[i+j+1], dp[i-j]+1);        }        for(j=0;i-j+1>=0 && i+j<s.length && s[i-j+1] === s[i+j]; j++) {            dp[i+j+1] = Math.min(dp[i+j+1], dp[i-j+1]+1);        }    }    return dp[s.length];}

The solution is to refer to the answer of the highest ticket. In the second layer of the cycle, if it is calculated dp[i+1], then certainly cannot directly judge is not a palindrome string, so this method did a very clever design: calculate dp[i+j+1] instead of calculation dp[i+1] . In this way, we can determine if J is a palindrome string when it is gradually increasing i-j ~ i+j .

It is easy to see that the time complexity of this solution isO(n^2)

The third question, https://leetcode.com/problems/word-break/

This question is relatively simple, or similar to the previous question, using dp[i] to indicate that the first element is not able to cut into words , and then here is a simple place than the previous question, the palindrome string bad judgment, here is not in the dictionary is easy to judge out, the specific solution is as follows:

var wordBreak = function(s, wordDict) {    var maxWordLength = 0;    wordDict.forEach(function(w){        maxWordLength = Math.max(w.length, maxWordLength);    });    var dp = [true];    for(var i=1;i<=s.length;i++) dp.push(false);    for(i=1;i<=s.length;i++) {        for(var j=1;j<=maxWordLength && j<=i;j++) {            if(wordDict.has(s.slice(i-j,i)) && dp[i-j]) {                dp[i] = true;                break;            }        }    }    return dp[s.length];};

Note that one place, the second loop, is that the maximum value of j is not I but maxwordlength, so the complexity of time is O(NL) not O(n^2) , where L is maxwordlength.

First of these relatively simple dynamic programming problems, in addition to palindrome string is difficult, the other is easy or Medium level, belongs to the issue of the need to do the bug free.

The next time you continue to talk about more complex dynamic programming problems, these topics are characterized by the examination of some operations between the two sequences.

Double sequence type:
There is no longest Common on Leetcode subsequence
https://leetcode.com/problems/edit-distance/
https://leetcode.com/problems/distinct-subsequences/
https://leetcode.com/problems/interleaving-string/

General algorithm collation: Dynamic programming

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.