A detailed Java explanation of classic questions in the dynamic programming series on Leetcode

During the house epidemic prevention, leetcode brushed more than ten linear dynamic programming algorithm questions, it is time to sum up and refine the DP idea.
I divided the summarized topics into two types of sequence matching and life.
Representative hot topics of the first category:
leetcode300. The longest ascending subsequence
leetcode53. The maximum subsequence and
leetcode1143. The longest common subsequence
leetcode72. Editing distance

Representative hot questions of the second category:
leetcode121. The best time to buy and sell stocks leetcode122. The best time to
buy and sell stocks II
leetcode322. Change change The
following are the ideas and code implementation of 7 questions

300. The longest ascending subsequenceInsert picture description here

  • Idea: Use an array to store the ascending sequence, the key is that the last output is the sequence length, so the length is guaranteed and there is no need to record the real suborder at all times.
class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length==0) return 0;
        for(int i=1; i<nums.length; i++){
            dp[1] = 1;
            for(int j=i-1; j>1; j--){
                 if(dp[j] < dp[i]) dp[j] = dp[j-1]+1; 
            }
        }
    }
}

The reason why I wrote this question first is because the greedy algorithm was used before, and the binary search is more difficult to think of

public static int lengthOfLIS(int[]nums) {
        if(nums.length < 2) return nums.length;
        //LIS数组存放最长子序列的数组,但并非时刻都存放的是最长子序列
        int[] LIS = new int[nums.length]; 
        LIS[0] = nums[0];//数组有负整数的情况
        int end = 0;
        for(int i=1; i<nums.length; i++){
            //如果该位元素大于子序列最后一位,上升子序列变长1
            if(nums[i]>LIS[end]){
                end++;   LIS[end]=nums[i];
            }
 //如果当前nums[i]小于子序列最后一位,则用二分法搜索子序列中比nums[i]大的最小数
            else{
                int left = 0,right =end;
                while(left<right){
                    int pivot = left+(right-left)/2;
                    if( LIS[pivot]< nums[i]){
                        left = pivot+1;
                    }
                    else{
                        assert LIS[pivot] >= nums[i];
                        right = pivot;
                    }
                }
                LIS[right]=nums[i];
            }
        }
        return end+1;
    }

LeetCode53. Maximum subsequence sum

  • Idea: The difficulty of the subsequence problem is the discontinuous sequence. Definition dp [i] indicates the degree of the most increasing subsequence ending in the number nums [i]. dp [i] depends on the result of dp [i-1], and each recursion needs to be recorded with max, and the final result is calculated from the bottom up. Let's use a simple example to push: nums = [1, -2,3], then dp [0] = nums [0] = 1;
  • step1: dp [1] = dp [0] + nums [1] = -1; at this time max = 1> dp [1], so the current max remains unchanged;
  • step2: dp [2] = dp [1] + nums [2] = 1; now max <dp [2], so max is updated to dp [2]
  • In this way, max is finally the animation of the maximum dp [i]
    algorithm idea
   public int maxSubArray(int[] nums) {
		int[] dp = new int[nums.length];
		dp[0] = nums[0];   
		int max = nums[0];
		for (int i = 1; i < nums.length; i++) {
		//nums[i] > 0,说明对结果有增益,dp[i]再加当前遍历值
		//nums[i] <= 0,说明对结果无增益,dp[i]直接更新为当前遍历数字
			dp[i] = Math.max(dp[i- 1] + nums[i], nums[i]);	
			if (max < dp[i]) {      //关键步:取每次遍历的当前最大和
				max = dp[i];
			}
		}
		return max;
   }

LeetCode1143. The longest common subsequence

Insert picture description hereInsert picture description here

  • Ideas:
    Here I recommend a younger brother who demonstrates algorithm ideas on a whiteboard on youtube. His video explanations are very detailed, which can not only help to understand thoroughly, but also improve English, the best of both worlds: [Detailed explanation of the LeetCode algorithm] The
    intermediate state of the longest common subsequence In a two-dimensional array, dp [i] [j] represents the position of the first i bits of string 1 and the first j bits of string 2.
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int n1 = text1.length(); int n2 = text2.length();
        int[][] dp = new int[n1+1][n2+1];
        for(int i=0; i<n1; i++) { dp[i][0] = 0; }
        for(int j =0; j<n2; j++){ dp[0][j] = 0; }
        for(int i=1; i<=n1; i++){
            for(int j =1; j<=n2; j++){
                if(text1.charAt(i-1)==text2.charAt(j-1))
                    dp[i][j] = dp[i-1][j-1] + 1;
                else{
                    dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
                }
            }
        }
        return dp[n1][n2];
    }
}

LeetCode72. Editing distance

topic

  • Idea: This type of solution to the dynamic programming problem of two strings can use the same method as the longest ascending subsequence, using two pointers i and j to point to the end of the two strings, and then step by step to narrow the problem. Scale.
    dp [i] [j] —The minimum number of steps required to convert the first i bits of word1 into the first j bits of word2 .
public int minDistance(String word1, String word2) {
        int n1 = word1.length(); int n2 = word2.length();
        int[][] dp = new int[n1+1][n2+1];
        for(int i=1; i<=n1; i++) dp[i][0] = dp[i-1][0] +1;   
        for(int j=1; j<=n2; j++) dp[0][j] = dp[0][j-1] +1;  
        for(int i=1; i<=n1; i++){
            for(int j=1; j<=n2; j++){
            //word1和word2的该位字符相同,不需要改动。
                if(word1.charAt(i-1)==word2.charAt(j-1))
                   dp[i][j] = dp[i-1][j-1];
            //如果字符不同,则取该步之前的状态基础上做删除,修改,插入中的最小改动
                else 
                   dp[i][j] = Math.min(Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;
            }
        }
        return dp[n1][n2];
    }

The problem of the stock series recommends a thought that is well summarized: a way to eliminate 6 stock problems
Insert picture description here

  • Idea: The profit gained in the previous i-day is the profit in the previous i-day; the i-th day is to subtract prices [i] from the profit if "buy stocks", and to give profits if "sell stocks" Increase prices [i]. The maximum profit at this time is the larger of the two possible choices.
    Therefore, there can be an optimal sub-structure:
    dp [i] [0] represents the profit owned when the stock is not held on the i-th day; dp [i] [0] = Math.max (dp [i-1] [0], dp [i-1] [1] + prices [i]);
    and dp [i] [1] represents the profit from holding the stock on the i-th day dp [i] [1] = Math.max (dp [i-1 ] [1], -prices [i]);
class Solution {
   public int maxProfit(int[] prices) {
        int n = prices.length;
        if(prices.length==0)return 0;
        int[][] dp = new int[n][2]; //行表示第 i天,列表示是否买入当天股票
        dp[0][0] = 0; //i = 0 时 dp[i-1] 是不合法的。
        dp[0][1] = -prices[0];
        for (int i = 1; i < n; i++) {
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i-1][1], -prices[i]);
        }
        return dp[n - 1][0];
    }
}

Insert picture description here
+ Idea: The idea is basically the same as the previous question. The difference is that the number of transactions changes from 1 to unlimited.
Then dp [i] [1] is equal to dp [i-1] [1] and dp [i-1] [0 ] -prices [i] The bigger result. Because there is also a buy transaction in the previous i days, that is, the profit obtained on the i day of buying stocks is directly related to the i-1.

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length==0) return 0;
        int[][] dp = new int[prices.length][2];
        dp[0][0] = 0; 
        dp[0][1] = -prices[0];
        for(int i=1; i<prices.length;i++){
              dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
              dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
        } 
        return dp[prices.length-1][0];
    }
}

leetcode322. Change change

Insert picture description here

  • Idea: The optimal state dp [i] represents the minimum number of coins required for the amount i. The problem to note is that each dp [] number must be greater than the total amount of money during initialization, which is an extreme case where coins are all 1.
class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];
      // 注意:因为要比较的是最小值,不可能是结果的初始化值就得赋一个最大值
        Arrays.fill(dp, amount + 1);
        dp[0] =0;
        for(int i=1; i<=amount; i++){
            for(int coin: coins){
        //如果可包含coin,那么剩余钱是i−coins,要兑换的硬币数是 dp[i−coins]+1
                if(coin <= i)
                   dp[i] = Math.min(dp[i],dp[i-coin]+1);
            }
        }
        return dp[amount]<=amount ? dp[amount] : -1;
    }
}

If this article is helpful to you, please like it and let me know, I will work harder to share good content with you, thank you ~

Published 27 original articles · praised 4 · visits 2178

Guess you like

Origin blog.csdn.net/smile001isme/article/details/105472635