The sword refers to the offer-Introduction to dynamic programming

Basic idea of ​​dynamic programming

dynamic programming algorithm

Usually used to solve problems with some optimal properties. In this type of problem, there may be many feasible solutions. Each solution corresponds to a value, and we want to find the solution with the optimal value .

The dynamic programming algorithm is similar to the divide -and-conquer method .

Different from the divide-and-conquer method, the sub-problems obtained by decomposing are often not independent of each other for problems that are suitable for solving by dynamic programming. If the divide-and-conquer method is used to solve such problems, the number of sub-problems obtained by decomposition is too many, and some sub-problems are repeatedly calculated many times. If we can save the answers to the solved sub-problems and find the solutions when we need them, we can avoid a lot of double-counting and save time.

We can use a table to record the answers to all solved subproblems. Regardless of whether the subproblem will be used later, as long as it has been computed, fill the table with its result. This is the basic idea of ​​dynamic programming. The specific dynamic programming algorithms vary, but they all have the same form-filling format.

simple question

The title of the article comes from LeeCode

1. Fibonacci sequence

The idea of ​​Fibonacci is very common in dynamic programming. Some classic problems are extensions of the application of the Fibonacci sequence. Later, we can see that the basic structure of such problems is the same.

Write a function, input n, to find the nth term of the Fibonacci sequence (ie F(N)). The Fibonacci sequence is defined as follows:

F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), where N > 1.

The Fibonacci sequence starts with 0 and 1, and the subsequent Fibonacci numbers are obtained by adding the previous two numbers.

Input: n = 2 Output: 1

----------------------------------analyze--------------- -------------------------------

The boundary conditions for Fibonacci numbers are F(0)=0 and F(1)=1. When n>1n>1n>1, the sum of each item is equal to the sum of the previous two items, so there is the following recurrence relation:

F(n)=F(n−1)+F(n−2)  

As for recursion, it can be solved by recursion or loop. It is worth noting here that only three values ​​​​of F(n), F(n-1), and F(n-2) are needed for operation, so you can only use Three variables to store, that is, rolling arrays

class Solution {
    public int fib(int n) {
         int a=0;
         int b=1;
         int c=0;
         if(n==0) return a;
         else if(n==1) return b;
         for(int i=n-1;i>0;i--){
            c=a+b
            a=b;
            b=c;
         }
         return c;
    }
}

Such questions require attention to the conditions under which the cycle begins.

2. Frog jumping steps

A frog can jump up 1 steps at a time, or 2 steps at a time. Find how many ways the frog can jump up a n step.

Input: n = 2 Output: 2

----------------------------------------------analyze--- ------------------------------------------------

It should be noted here that the choice of frog jumping steps is in order, such as jumping one step first, then jumping two steps, which is different from jumping two steps first and then jumping one step.

If you start thinking about whether the frog takes one step or two steps, it is easy to fall into a dead end. You can think in the opposite direction.

Suppose the frog has now jumped up the nth step. It looks back and thinks about how it came up, whether it jumped up two steps or one step. If it jumped up one step, it was on the n-1 step before jumping. In the same way, before jumping, it may also be on the n-2 steps, and combined with what was said at the beginning, different sequences cannot be counted as one step, and the last step is different. The two jumping methods of the frog are completely independent from the previous ones. .

a binary tree

Obviously, the nth step of the frog is equal to the n-1 step plus the n-2 step, which is a Fibonacci sequence

The same as the above solution, but it should be noted that a, b, that is, the position at the beginning of the scrolling array, determines the position at which the loop starts

class Solution {
    public int numWays(int n) {
        if(n==0) return 1;
        int count=1;
        int a=1;
        int b=2;
        int c=1;
        if(n==2) return 2;
        for(int i=n-2;i>0;i--){
            c=a+b
            a=b;
            b=c;
        }
        return c;
    }
}

3. Maximum sum of consecutive subarrays

Input an integer array, one or more consecutive integers in the array form a subarray. Find the maximum value of the sum of all subarrays. The required time complexity is O(n).

Input: nums = [-2,1,-3,4,-1,2,1,-5,4] 
Output: 6 
Explanation: The maximum sum of consecutive subarrays [4,-1,2,1] is 6.

----------------------------------------analyze--------- -------------------------------------------------- -----

The first time you encounter this kind of problem, you may fall into a misunderstanding. For example, if you traverse the array, although the number is negative, the value of the array will become smaller, but there is a relatively large positive value behind it. The upper positive value can completely offset the negative value. How to look at two or more values ​​at the same time to make a trade-off, where should the front and rear boundaries be?

When this kind of dilemma is between looking ahead and looking back, you can think about decomposing the problem and use dynamic programming or divide-and-conquer algorithm to substitute it.

We can see that the answer only needs the largest sum, not continuous sub-arrays, so can we try to use a variable to store the maximum value, and then traverse all the sub-arrays to find out, obviously it is not possible, then we can extend it.

All sub-arrays, and most sub-arrays have many overlapping parts, such as the two sub-arrays [1,2] and [1,2,3]. At this time, it can be found that the latter is only one more than the former by 3 , and 3 is a positive value, so the latter subarray must be larger than the first.

Combined with the idea of ​​dynamic programming, we regard 3 as i, and we use f(i) to represent the maximum sum of consecutive subarrays ending with the i-th number ,

And according to the previous discussion, the value of f(i) can be derived from f(i-1), such as [1,2] and [1,2,3]

结论:f(i)= max{  f(i−1) + nums[i]  ,  nums[i]  }

The meaning of num[i] is nums[i]+0, plus the previous sub-array is smaller than itself, and discarded directly.

Then it is to traverse the array to find each f(i), which in turn includes the method of the Fibonacci sequence, and finally get the largest f(i)

class Solution {
    public int maxSubArray(int[] nums) {
        int pre = 0, maxAns = nums[0];
        for (int x : nums) {
            pre = Math.max(pre + x, 0+x);
            maxAns = Math.max(maxAns, pre);
        }
        return maxAns;
    }
}

difficult question

1. Maximum Profits from Stocks

Assuming that the price of a stock is stored in an array in chronological order, what is the maximum profit that can be obtained by buying and selling the stock once?

Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (stock price = 1), sell on day 5 (stock price = 6), Maximum profit = 6-1 = 5.
     Note that the profit cannot be 7-1 = 6, because the sell price needs to be greater than the buy price.

-------------------------------------------------- ---analyze---------------------------------------------- -------

The way of thinking is similar to the previous question

First of all, the brute force method can be done, but it is very troublesome, such as traversing each value, and then traversing the smallest value in front of this value, comparing each difference to draw a conclusion, and the gradual progress of the traversal can solve the problem of time.

From the perspective of dynamic programming, the brute force method can be modified, and the minimum value of all the previous values ​​can be obtained without traversal, otherwise it is perfect, then define a variable to store the minimum value when traversing the entire array, and then In the definition of a worst value maximum value, used to compare the difference.

The point is how to assign and consume two values ​​perfectly and elegantly

public class Solution {
    public int maxProfit(int prices[]) {
        int minprice = Integer.MAX_VALUE;
        int maxprofit = 0;
        for (int i = 0; i < prices.length; i++) {
            if (prices[i] < minprice) {
                minprice = prices[i];
            } else if (prices[i] - minprice > maxprofit) {
                maxprofit = prices[i] - minprice;
            }
        }
        return maxprofit;
    }
}

2. The maximum value of the gift

On each square of an m*n chessboard there is a gift, and each gift has a certain value (value greater than 0). You can start taking gifts from the top left corner of the board and move to the right or down one square at a time until you reach the bottom right corner of the board. Given the value of a chessboard and the gifts on it, calculate the maximum value of gifts you can get?

Input: 
[
  [1,3,1],
  [1,5,1] , [4,2,1
  ]
]
Output: 12
Explanation: The path 1→3→5→2→1 can get the most valuable gift

-----------------------------analyze-------------------- --------------------------------------

First of all, it can be determined that we have to traverse the entire array to get the maximum value. Then when we start thinking from the upper left corner, we do not know which step to take, and it is not easy to make a decision. Of course, traversing the entire array can also be stored in a binary tree. Solve the problem

Therefore, we start thinking from the lower right corner, and we can see that there are only two ways to reach the lower right corner. Which way you choose depends on which way has more gifts, but if you can't see all the previous roads, it will be the same as from the upper left corner. the predicament?

We can start thinking according to the ideas mentioned above. Each step is related to all the previous steps, and each step can be derived from the previous step. This is not the method of the Fibonacci sequence. This problem can be regarded as a two-dimensional the sum of contiguous subarrays of

Derive the equation first, and consider the boundary problem

When i=0, it is the starting element;
when i=0 and j≠0, it is the first row element of the matrix, which can only be reached from the left;
when i≠0 and j=0, it is the first column element of the matrix , can only be reached from the top;
when i≠0 and j≠0, it can be reached from the left or above;

 Then there's the code problem

class Solution {
    public int maxValue(int[][] grid) {
        int m=grid.length;
        int n=grid[0].length;
        int max=grid[0][0];
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(i==0 && j==0){
                    continue;
                }else if(i==0){
                    grid[0][j]=grid[0][j-1]+grid[0][j];
                }
                else if(j==0){
                    grid[i][0]=grid[i-1][0]+grid[i][0];
                }else{
                    grid[i][j]=Math.max(grid[i-1][j],grid[i][j-1])+grid[i][j];
                }
            }
        }
    return grid[m-1][n-1];
    }
}

 3. Translate numbers into strings

Given a number, we translate it into a string according to the following rules: 0 is translated to "a", 1 is translated to "b", ..., 11 is translated to "l", ..., 25 is translated to "z". A number may have multiple translations. Please program a function to calculate how many different translation methods a number has 

Input: 12258

Output: 5
Explanation: 12258 with 5 different translations, "bccfi", "bwfi", "bczi", "mcfi" and "mzi"

-------------------------------------analyze------------ -------------------------------------------------- ------------

Obviously, it is a question of choosing one or two, that is, the enhanced version of the frog jumping steps. The difference is that if the "two steps" greater than 25 need to be canceled, the scroll array can also be used to save space.

class Solution {
    public int translateNum(int num) {
        String src = String.valueOf(num);
        int p = 0, q = 0, r = 1;
        for (int i = 0; i < src.length(); ++i) {
            p = q; 
            q = r; 
            r = 0;
            r += q;
            if (i == 0) {
                continue;
            }
            String pre = src.substring(i - 1, i + 1);
            if (pre.compareTo("25") <= 0 && pre.compareTo("10") >= 0) {
                r += p;
            }
        }
        return r;
    }
}

Guess you like

Origin blog.csdn.net/xsj5211314/article/details/123881886