[Algorithm Question] Jumping Game, Maximum Subarray Sum, and Decoding Method in the Intermediate Stage of Dynamic Programming

foreword

Dynamic programming (Dynamic Programming, DP for short) is a method to solve the optimization problem of multi-stage decision-making process. It is a strategy for decomposing complex problems into overlapping subproblems, and deriving the optimal solution to the problem by maintaining the optimal solution for each subproblem.

The main idea of ​​dynamic programming is to use the optimal solution of the solved sub-problem to derive the optimal solution of a larger problem, thus avoiding repeated calculations. Therefore, dynamic programming usually uses a bottom-up approach to solve small-scale problems first, and then gradually derive larger-scale problems until the optimal solution of the entire problem is solved.

Dynamic programming usually includes the following basic steps:

  1. Define the state: Divide the problem into several sub-problems, and define the state to represent the solution of the sub-problems;
  2. Define the state transition equation: According to the relationship between the sub-problems, design the state transition equation, that is, how to deduce the calculation process of the unknown state from the known state;
  3. Determine the initial state: define the solution of the smallest sub-problem;
  4. Bottom-up solution: Calculate the optimal solution of all states according to the state transition equation;
  5. Constructs the solution of the problem from the optimal solution.

Dynamic programming can solve many practical problems, such as shortest path problem, knapsack problem, longest common subsequence problem, edit distance problem, etc. At the same time, dynamic programming is also the core idea of ​​many other algorithms, such as divide and conquer algorithm, greedy algorithm, etc.

Dynamic programming is a method to solve the optimization problem of multi-stage decision-making process. It decomposes complex problems into overlapping sub-problems, and derives the optimal solution of the problem by maintaining the optimal solution of each sub-problem. Dynamic programming includes steps such as defining states, designing state transition equations, determining initial states, bottom-up solutions, and constructing problem solutions. Dynamic programming can solve many practical problems, and it is also one of the core ideas of other algorithms.

1. The largest subarray sum

Given an integer array nums, please find a continuous subarray with the largest sum (the subarray contains at least one element), and return its largest sum.

A subarray is a contiguous part of an array.

Example 1:

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

Example 2:

input: nums = [1]
output: 1

Example 3:

Input: nums = [5,4,-1,7,8]
Output: 23

1.1. Ideas

Suppose the length of the nums array is n, and the subscripts range from 0 to n−1.

Use f(i) to represent the "maximum sum of consecutive sub-arrays" ending with the i-th number, then the required answer is: the
insert image description here
realization of time complexity O(n) and space complexity O(n), that is, use an f An array is used to store the values ​​of f(i), and a loop is used to obtain all
f(i). Considering that f(i) is only related to f(i−1), we can use only one variable pre to maintain the value of f(i−1) for the current f(i), thereby reducing the space complexity To O(1), this is a bit similar to the idea of ​​​​"rolling arrays".

1.2, code implementation

class Solution {
    
    
public:
    int maxSubArray(vector<int>& nums) {
    
    
        int pre = 0, maxAns = nums[0];
        for (const auto &x: nums) {
    
    
            pre = max(pre + x, x);
            maxAns = max(maxAns, pre);
        }
        return maxAns;
    }
};

2. Jumping game

Given an array nums of non-negative integers, you are initially at the first index of the array.

Each element in the array represents the maximum length you can jump at that position.

Determine whether you can reach the last subscript.

Example 1:

Input: nums = [2,3,1,1,4]
Output: true
Explanation: You can jump 1 step first, from subscript 0 to subscript 1, and then jump 3 steps from subscript 1 to the last subscript.

Example 2:

Input: nums = [3,2,1,0,4]
Output: false
Explanation: No matter what, the position with subscript 3 will always be reached. But the maximum jump length of this subscript is 0, so it is never possible to reach the last subscript.

Source: LeetCode.

2.1. Ideas

State representation: Let f[i] denote whether i can be reached.

Recursive analysis: When considering the reachability of f[i], it can be found that if i is reachable, it must have jumped directly from a previous reachable point j, and the distance between ji is less than or equal to The value of the number of steps corresponding to j, if there is a point that can be skipped directly before i, it means that i is reachable, otherwise i is not reachable, so we can traverse all the points before i.

Recursion formula:

f[i] = (f[i - 1] && nums[i - 1] >= 1) || (f[i - 2] && nums[i - 2] >= 2)......|| (f[0] && nums[0] >= i)

Initially f[0] = true, and then recursively from front to back.

Optimization: Note that if i is unreachable, then all points after i must also be unreachable, because all points before i cannot reach i at the farthest, and they must not reach the point after i. Although the time complexity is still O ( n 2 ) O(n^2)O ( n2 )But avoids a lot of double counting.

2.2. Code implementation

class Solution {
    
    
public:

    bool canJump(vector<int>& nums) {
    
    
        int l = nums.size();
        vector<bool> f(l);
        f[0] = true;

        for(int i = 1; i < l; i ++)
        {
    
    
            for(int j = i - 1; j >= 0; j --)
                if(f[j] && nums[j] >= i - j)
                {
    
    
                   f[i] = true;
                   break; 
                }
            //如果i不可达,则直接退出循环,i后面的点都不可达
            if(!f[i]) break;
        }

        return f[l - 1];                                 
    }
};

Summary: Use dp[i] to indicate whether i can be reached. For example [2,3,1,1,4], initialize dp[0] = true, consider the first element 2, then dp[1] = true, dp[2] = true; traverse the next element 3; 3 can be reached, then dp[2] = true dp[3] = true dp[4] = true, and so on.

3. Decoding method

A message containing the letters AZ is encoded by the following mapping:

'A' -> "1"
'B' -> "2"
...
'Z' -> "26"

To decode an encoded message, all numbers must be reverse-mapped back to letters based on the method of mapping above (there may be more than one method). For example, "11106" could be mapped as:

  • "AAJF", group messages as (1 1 10 6)
  • "KJF", group the message as (11 10 6)
    Note that the message cannot be grouped as (1 11 06) because "06" cannot be mapped to "F", because "6" and "06" are not in the mapping Not equivalent.

Given a non-empty string s containing only numbers, please count and return the total number of decoding methods.

The question data guarantees that the answer must be a 32-bit integer.

Example 1:

Input: s = "12"
Output: 2
Explanation: It can be decoded as "AB" (1 2) or "L" (12).

Example 2:

Input: s = "226"
Output: 3
Explanation: It can be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

Example 3:

Input: s = "06"
Output: 0
Explanation: "06" cannot be mapped to "F" because of leading zeros ("6" and "06" are not equivalent).

Source: LeetCode.

3.1. Ideas

For a given string s, let its length be n, and the characters in it are s[1], s[2], ⋯, s[n] from left to right. The number of decoding methods for string s can be calculated using dynamic programming.

fi represents the number of decoding methods for the first i characters s[1…i] of the string s. When performing state transition, we can consider which characters in s were used for the last decoding, then there will be the following two situations:

  • The first case is that we use a character, that is, s[i] for decoding, so as long as s[i]≠0, it can be decoded into a letter in A∼I. Since the number of decoding methods for the remaining first i−1 characters is f (i−1), the state transition equation can be written:
    insert image description here
  • The second case is that we use two characters, namely s[i−1] and s[i] for encoding. Similar to the first case, s[i−1] cannot be equal to 0, and the integer composed of s[i−1] and s[i] must be less than or equal to 26, so that they can be decoded into some letters. Since the number of decoding methods for the remaining first i−2 characters is f (i−2), the state transition equation can be written:
    insert image description here

3.2. Code implementation

class Solution {
    
    
public:
    int numDecodings(string s) {
    
    
        int n = s.size();
        // a = f[i-2], b = f[i-1], c = f[i]
        int a = 0, b = 1, c;
        for (int i = 1; i <= n; ++i) {
    
    
            c = 0;
            if (s[i - 1] != '0') {
    
    
                c += b;
            }
            if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) {
    
    
                c += a;
            }
            tie(a, b) = {
    
    b, c};
        }
        return c;
    }
};

Time complexity: O(n).
Space complexity: O(1).

Summarize

Dynamic programming (Dynamic Programming) is a method to solve multi-stage decision-making optimization problems. It decomposes complex problems into overlapping sub-problems and derives the optimal solution of the problem by maintaining the optimal solution of each sub-problem. Dynamic programming can solve many practical problems, such as shortest path problem, knapsack problem, longest common subsequence problem, edit distance problem, etc.

The basic idea of ​​dynamic programming is to use the optimal solution of the solved sub-problem to derive the optimal solution of a larger problem, thus avoiding repeated calculations. It usually uses a bottom-up approach to solve small-scale problems first, and then gradually derive larger-scale problems until the optimal solution of the entire problem is solved.

Dynamic programming usually includes the following basic steps:

  1. Define the state: Divide the problem into several sub-problems, and define the state to represent the solution of the sub-problems;
  2. Define the state transition equation: According to the relationship between the sub-problems, design the state transition equation, that is, how to deduce the calculation process of the unknown state from the known state;
  3. Determine the initial state: define the solution of the smallest sub-problem;
  4. Bottom-up solution: Calculate the optimal solution of all states according to the state transition equation;
  5. Constructs the solution of the problem from the optimal solution.

The time complexity of dynamic programming is usually O ( n 2 ) O(n^2)O ( n2 )orO(n3)O(n^3)O ( n3 ), the space complexity is O(n), where n represents the scale of the problem. In practical applications, in order to reduce space complexity, techniques such as rolling arrays can usually be used to optimize dynamic programming algorithms.

insert image description here

Guess you like

Origin blog.csdn.net/Long_xu/article/details/131464594