leetcode lecture on algorithm interview in Dachang 3. Dynamic programming

Keywords: Algorithm leetcode Interview

leetcode lecture on algorithm interview in Dachang 3. Dynamic programming

Video tutorial (efficient learning): Click to learn

catalog:

1. Introduction

2. Time and space complexity

3. Dynamic planning

4. Greed

5. Binary search

6. Depth first & breadth first

7. Double pointer

8. Sliding window

9. Bit operation

10. Recursion & divide and conquer

11 Pruning & backtracking

12. Reactor

13. Monotone stack

14. Sorting algorithm

15. Linked list

16.set&map

17. Stack

18. Queue

19. Array

20. String

21. Trees

22. Dictionary tree

23. Consolidation

24. Other types of questions

What is dynamic programming

Dynamic Programming, English: Dynamic Programming, referred to as DP, decomposes the problem into overlapping subproblems. Solving the original problem by repeatedly solving subproblems is Dynamic Programming. If a problem has many overlapping subproblems, it is more effective to use Dynamic Programming to solve it.

The core problem of solving dynamic programming is exhaustion, but this kind of problem is a little special because there are "overlapping subproblems" in this kind of problem. If it is brutally exhausted, the efficiency will be extremely low. The dynamic programming problem must have an "optimal substructure" in order to get the maximum value of the original problem through the maximum value of the subproblem. In addition, although the core idea of dynamic programming is to seek the maximum value by exhaustive method, the problem can be changeable. In fact, it is not easy to enumerate all feasible solutions. Only by listing * * the correct "state transition equation * * can we enumerate them correctly. Overlapping subproblem, optimal substructure and state transition equation are the three elements of dynamic programming

Differences between dynamic programming and other algorithms

  1. The difference between dynamic programming and divide and Conquer: both dynamic programming and divide and conquer have optimal substructures, but the sub problems of divide and conquer do not overlap
  2. The difference between dynamic programming and greedy: every state in dynamic programming must be derived from the previous state, which is different from greedy. Greedy has no state derivation, but directly selects the optimal solution from the local, so it is always the local optimal, but the global solution is not necessarily the optimal.
  3. The difference between dynamic programming and recursion: there may be a lot of repeated calculations between recursion and backtracking. Dynamic programming can reduce unnecessary repeated calculations by recursion and memory

Problem solving method of dynamic programming

  • Recursion + memorization (top-down)
  • Dynamic programming (bottom-up)

Steps for solving dynamic programming problems

  1. Define status according to overlapping subproblems
  2. Finding the optimal substructure and deriving the state transition equation
  3. Determine dp initial state
  4. Determine output value

The solution of Fibonacci's dynamic programming

The animation is too large. Click to view it

Violent recursion
//Violent recursion complexity O(2^n)
var fib = function (N) {
    if (N == 0) return 0;
    if (N == 1) return 1;
    return fib(N - 1) + fib(N - 2);
};
Recursion + memorization
var fib = function (n) {
    const memo = {}; // Cache the calculated results

    const helper = (x) => {
        if (memo[x]) return memo[x];
        if (x == 0) return 0;
        if (x == 1) return 1;
        memo[x] = fib(x - 1) + fib(x - 2);
        return memo[x];
    };

    return helper(n);
};


dynamic programming
const fib = (n) => {
    if (n <= 1) return n;
    const dp = [0, 1];
    for (let i = 2; i <= n; i++) {
        //Calculate each state from the bottom up
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
};

Rolling array optimization
const fib = (n) => {
    if (n <= 1) return n;
    //The rolling array dp[i] is only related to dp[i-1] and dp[i-2]. It only maintains the rolling array with length 2 and constantly replaces the array elements
    const dp = [0, 1];
    let sum = null;
    for (let i = 2; i <= n; i++) {
        sum = dp[0] + dp[1];
        dp[0] = dp[1];
        dp[1] = sum;
    }
    return sum;
};
Dynamic programming + dimensionality reduction, (dimensionality reduction can reduce spatial complexity, but it is not conducive to program expansion)
var fib = function (N) {
    if (N <= 1) {
        return N;
    }
    let prev2 = 0;
    let prev1 = 1;
    let result = 0;
    for (let i = 2; i <= N; i++) {
        result = prev1 + prev2; //Just use two variables
        prev2 = prev1;
        prev1 = result;
    }
    return result;
};

509. Fibonacci number(easy)

Method 1. Dynamic programming
  • Idea: bottom-up dynamic programming
  • Complexity analysis: time complexity O(n), space complexity O(1)

Js:

var fib = function (N) {
    if (N <= 1) {
        return N;
    }
    let prev2 = 0;
    let prev1 = 1;
    let result = 0;
    for (let i = 2; i <= N; i++) {
        result = prev1 + prev2;
        prev2 = prev1;
        prev1 = result;
    }
    return result;
};


Java:

class Solution {
    public int fib(int n) {
        if (n <= 1) {
            return n;
        }
        int prev2 = 0, prev1 = 1, result = 0;
        for (int i = 2; i <= n; i++) {
            result = prev2 + prev1;
            prev2 = prev1; 
            prev1 = result; 
        }
        return result;
    }
}

62. Different paths (medium)

Method 1. Dynamic programming

The animation is too large. Click to view it

  • Idea: since each position can only be down or right, the path sum of each coordinate is equal to the sum of different paths at the same position in the previous row and the same position in the previous column. The state transition equation: f[i][j] = f[i - 1][j] + f[i][j - 1];
  • Complexity: time complexity O(mn). Spatial complexity O(mn), optimized O(n)

js:

var uniquePaths = function (m, n) {
    const f = new Array(m).fill(0).map(() => new Array(n).fill(0)); //Initial dp array
    for (let i = 0; i < m; i++) {
        //Initialize column
        f[i][0] = 1;
    }
    for (let j = 0; j < n; j++) {
        //Initialization line
        f[0][j] = 1;
    }
    for (let i = 1; i < m; i++) {
        for (let j = 1; j < n; j++) {
            f[i][j] = f[i - 1][j] + f[i][j - 1];
        }
    }
    return f[m - 1][n - 1];
};

//State compression
var uniquePaths = function (m, n) {
    let cur = new Array(n).fill(1);
    for (let i = 1; i < m; i++) {
        for (let r = 1; r < n; r++) {
            cur[r] = cur[r - 1] + cur[r];
        }
    }
    return cur[n - 1];
};


Java:

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] f = new int[m][n];
        for (int i = 0; i < m; ++i) {
            f[i][0] = 1;
        }
        for (int j = 0; j < n; ++j) {
            f[0][j] = 1;
        }
        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                f[i][j] = f[i - 1][j] + f[i][j - 1];
            }
        }
        return f[m - 1][n - 1];
    }
}

//State compression
class Solution {
    public int uniquePaths(int m, int n) {
        int[] cur = new int[n];
        Arrays.fill(cur,1);
        for (int i = 1; i < m;i++){
            for (int j = 1; j < n; j++){
                cur[j] += cur[j-1] ;
            }
        }
        return cur[n-1];
    }
}

63. Different paths II(medium)

Method 1. Dynamic programming
  • Idea: like question 62, the difference is to return to 0 directly when encountering obstacles
  • Complexity: time complexity O(mn), space complexity O(mn), state compression followed by o(n)

Js:

var uniquePathsWithObstacles = function (obstacleGrid) {
    const m = obstacleGrid.length;
    const n = obstacleGrid[0].length;
    const dp = Array(m)
        .fill()
        .map((item) => Array(n).fill(0)); //Initial dp array

    for (let i = 0; i < m && obstacleGrid[i][0] === 0; ++i) {
        //Initial column
        dp[i][0] = 1;
    }

    for (let i = 0; i < n && obstacleGrid[0][i] === 0; ++i) {
        //Initial row
        dp[0][i] = 1;
    }

    for (let i = 1; i < m; ++i) {
        for (let j = 1; j < n; ++j) {
            //Return 0 directly when encountering obstacles
            dp[i][j] = obstacleGrid[i][j] === 1 ? 0 : dp[i - 1][j] + dp[i][j - 1];
        }
    }

    return dp[m - 1][n - 1];
};

//State compression
var uniquePathsWithObstacles = function (obstacleGrid) {
    let m = obstacleGrid.length;
    let n = obstacleGrid[0].length;
    let dp = Array(n).fill(0); //Fill with 0 because there are obstacles. The value of the current dp array element is also related to obstacleGrid[i][j]
    dp[0] = 1; //The first column is temporarily filled with 1
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            if (obstacleGrid[i][j] == 1) {
                //Pay attention to the conditions. When encountering obstacles dp[j] it becomes 0. The first column is included here
                dp[j] = 0;
            } else if (j > 0) {
                //Only when J > 0 is not the first column can j - 1 be obtained
                dp[j] += dp[j - 1];
            }
        }
    }
    return dp[n - 1];
};


Java:

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int n = obstacleGrid.length, m = obstacleGrid[0].length;
        int[] dp = new int[m];

        dp[0] = obstacleGrid[0][0] == 0 ? 1 : 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (obstacleGrid[i][j] == 1) {
                    dp[j] = 0;
                    continue;
                }
                if (j - 1 >= 0 && obstacleGrid[i][j - 1] == 0) {
                    dp[j] += dp[j - 1];
                }
            }
        }
        
        return dp[m - 1];
    }
}

70. Climb stairs (medium)

Method 1. Dynamic programming

  • Idea: because you can climb 1 or 2 steps at a time, you can go up from n-2 or n-1 to the nth step, which is actually Fibonacci's dp equation
  • Complexity analysis: time complexity O(n), space complexity O(1)

Js:

var climbStairs = function (n) {
    const memo = [];
    memo[1] = 1;
    memo[2] = 2;
    for (let i = 3; i <= n; i++) {
        memo[i] = memo[i - 2] + memo[i - 1];//Therefore, to the nth step, you can come up from n-2 or n-1
    }
    return memo[n];
};

//State compression
var climbStairs = (n) => {
    let prev = 1;
    let cur = 1;
    for (let i = 2; i < n + 1; i++) {
        [prev, cur] = [cur, prev + cur]
        // const temp = cur;   //  Staging the last cur
        // cur = prev + cur;   //  Current cur = last cur + last cur
        // prev = temp;        // prev is updated to the last cur
    }
    return cur;
}

Java:

class Solution {
    public int climbStairs(int n) {
        int prev = 1, cur = 1;
        for (int i = 2; i < n + 1; i++) {
        int temp = cur;
        cur = prev + cur;  
        prev = temp; 
        }
        return cur;
    }
}

279. Complete square (medium)

Method 1: dynamic programming
  • Idea: dp[i] represents the minimum number of the complete square sum of I, dp[i - j * j] + 1 represents the number after subtracting the complete square of a complete square j and adding 1 equals dp[i]. As long as you find a smaller value in dp[i], dp[i - j * j] + 1, it is the value of the last dp[i].

  • Complexity: time complexity O(n* sqrt(n)), n is the input integer, which needs to be cycled n times. The complexity sqrt(n) and space complexity O(n) of dp equation are calculated each time

js:

var numSquares = function (n) {
    const dp = [...Array(n)].map((_) => 0); //Initialize dp array when n is 0
    for (let i = 1; i <= n; i++) {
        dp[i] = i; // The worst case is to + 1 every time, for example: dp[3]=1+1+1
        for (let j = 1; i - j * j >= 0; j++) {//Enumerate previous states
            dp[i] = Math.min(dp[i], dp[i - j * j] + 1); // Dynamic transfer equation
        }
    }
    return dp[n];
};

java:

class Solution {
    public int numSquares(int n) {
        int[] dp = new int[n];
        for (int i = 1; i <= n; i++) {
            dp[i] = i;
            for (int j = 1; i - j * j >= 0; j++) { 
                dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
            }
        }
        return dp[n];
    }
}

120. Triangle minimum path and(medium)

Method 1. Dynamic programming

  • Idea: traverse upward from the last layer of the triangle. The minimum path sum of each number is the smaller of the two numbers below it plus itself
  • Complexity analysis: time complexity O(n^2), space complexity O(n)

Js:

const minimumTotal = (triangle) => {
    const h = triangle.length;
    // Initialize dp array
    const dp = new Array(h);
    for (let i = 0; i < h; i++) {
        dp[i] = new Array(triangle[i].length);
    }

    for (let i = h - 1; i >= 0; i--) { //Bottom up traversal
        for (let j = 0; j < triangle[i].length; j++) { //On the same floor
            if (i == h - 1) {  // base case bottom layer
                dp[i][j] = triangle[i][j];
            } else { // The state transition equation is calculated from the upper layer to the lower layer
                dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j];
            }
        }
    }
    return dp[0][0];
};


//State compression
const minimumTotal = (triangle) => {
    const bottom = triangle[triangle.length - 1];
    const dp = new Array(bottom.length);
    // base case is the last line
    for (let i = 0; i < dp.length; i++) {
        dp[i] = bottom[i];
    }
    // Iterate from the penultimate column
    for (let i = dp.length - 2; i >= 0; i--) {
        for (let j = 0; j < triangle[i].length; j++) {
            dp[j] = Math.min(dp[j], dp[j + 1]) + triangle[i][j];
        }
    }
    return dp[0];
};


Java:

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int n = triangle.size();
        int [] dp = new int [n];
        for(int i = 0 ; i < n ; i++){
            dp[i] = triangle.get(n-1).get(i);
        }
        for(int i = n-2 ; i >= 0 ; i--){
            for(int j = 0 ; j <= i ; j++){
                dp[j] = triangle.get(i).get(j) + Math.min(dp[j] , dp[j+1]);//iteration
            }
        }
        return dp[0];
    }
}

152. Product maximum subarray (medium)

Method 1. Dynamic programming

  • Idea:

    1. Status definition: dp[i][0] represents the minimum product of subarrays from item 0 to item I, and dp[i][1] represents the maximum product of subarrays from item 0 to item I

    2. Initial state: dp[0][0]=nums[0], dp[0][1]=nums[0]

    3. Discussion by case:

      • Don't ride with others, just nums[i] yourself
      • num[i] is a negative number. I want to multiply it by the previous maximum product
      • num[i] is a positive number. I want to multiply it by the previous minimum product
    4. State transition equation:

      • dp[i] [0]=min(dp[i−1] [0]∗num[i] , dp[i−1] [1] ∗ num[i], num[i])
      • dp[i] [1]=max(dp[i−1] [0]∗num[i] , dp[i−1] [1] ∗ num[i], num[i])
    5. State compression: dp[i][x] is only associated with dp[i][x]-1, so only two variables need to be defined, prevmin = num [0], prevmax = num [0]

    6. Equation after state compression:

      • prevMin = Math.min(prevMin * num[i], prevMax * num[i], nums[i])
      • prevMax = Math.max(prevMin * num[i], prevMax * num[i], nums[i])
  • Complexity: time complexity O(n), space complexity O(1)

js:

var maxProduct = (nums) => {
    let res = nums[0]
    let prevMin = nums[0]
    let prevMax = nums[0]
    let temp1 = 0, temp2 = 0
    for (let i = 1; i < nums.length; i++) {
        temp1 = prevMin * nums[i]
        temp2 = prevMax * nums[i]
        prevMin = Math.min(temp1, temp2, nums[i])
        prevMax = Math.max(temp1, temp2, nums[i])
        res = Math.max(prevMax, res)
    }
    return res
}

Java:

class Solution {
    public int maxProduct(int[] nums) {
        int res = nums[0], prevMin = nums[0], prevMax = nums[0];
        int temp1 = 0, temp2 = 0;
        for (int i = 1; i < nums.length; i++) {
            temp1 = prevMin * nums[i];
            temp2 = prevMax * nums[i];
            prevMin = Math.min(Math.min(temp1, temp2), nums[i]);
            prevMax = Math.max(Math.max(temp1, temp2), nums[i]);
            res = Math.max(prevMax, res);
        }
        return res;
    }
}

Buying and selling stocks

121. The best time to buy and sell stocks (easy) limited number of transactions k=1

122. The best time to buy and sell stocks II (medium) unlimited number of transactions k = +infinity

123. The best time to buy and sell stocks III (hrad) limited number of transactions k=2

188. The best time to buy and sell stocks IV (hard) limit the number of transactions to k

309. The best time to buy and sell stocks includes the freezing period (medium) contains the transaction freeze period

714. The best time to buy and sell stocks includes handling fees (medium) each transaction includes handling fee

Questions 5 and 6 are equivalent to adding the conditions of freezing period and handling fee on the basis of question 2.

Restrictions
  • Buy before you sell
  • You cannot participate in multiple transactions at the same time. When you buy again, you need to sell first
  • Only when k > = 0 can transactions be carried out, otherwise there are no transaction times
Define operation
  • purchase
  • sell out
  • No operation
Define status
  • i: Days
  • k: The number of transactions. Each transaction includes buying and selling. Here, we only need to change k - 1 when buying
  • 0: do not hold shares
  • 1: Holding shares
give an example
dp[i][k][0]//On day i, you can trade k times without stocks in your hands
dp[i][k][1]//You can also trade stocks in your hand k times on day i

The ultimate maximum return is dp[n - 1][k][0] instead of dp[n - 1][k][1], because selling on the last day must be higher than holding

State transition equation
// There are two situations in which no stocks are held today
// 1. dp[i - 1][k][0], not held yesterday, not operated today. 
// 2. dp[i - 1][k][1] + prices[i] held it yesterday, sold it today, and there are no stocks in hand today.
dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])


// There are two situations for holding stocks today:
// 1.dp[i - 1][k][1] held yesterday, not operated today
// 2.dp[i - 1][k - 1][0] - prices[i] did not hold yesterday, but bought today.
dp[i][k][1] = Math.max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])

//The maximum profit is the maximum of these two cases

121. The best time to buy and sell stocks (easy) limited number of transactions k=1

State transition equation

//Not holding on day i is transferred from the maximum values of not holding and then not operating on day i-1 and holding and then selling on day i-1
dp[i][1][0] = Math.max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i])
//The holding on day i is transferred from the maximum values of holding and then not operating on day i-1 and not holding and then buying on day i-1
dp[i][1][1] = Math.max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i])
            = Math.max(dp[i - 1][1][1], -prices[i]) // When k=0, there are no transaction times, dp[i - 1][0][0] = 0

k is a fixed value of 1, which does not affect the result, so it can be ignored. After simplification, it is as follows

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])

Complete code

//Time complexity O(n) space complexity O(n), the second dimension of dp array is constant
const maxProfit = function (prices) {
    let n = prices.length;
    let dp = Array.from(new Array(n), () => new Array(2));
    dp[0][0] = 0; //Not held on day 0
    dp[0][1] = -prices[0]; //Held on day 0
    for (let 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];
};


State compression, dp[i] is only related to dp[i - 1], remove one dimension

//Time complexity O(n) space complexity O(1)
const maxProfit = function (prices) {
    let n = prices.length;
    let dp = Array.from(new Array(n), () => new Array(2));
    dp[0] = 0;
    dp[1] = -prices[0];
    for (let i = 1; i < n; i++) {
        dp[0] = Math.max(dp[0], dp[1] + prices[i]);
        dp[1] = Math.max(dp[1], -prices[i]);
    }
    return dp[0];
};

//Semantics
const maxProfit = function (prices) {
    let n = prices.length;
    let sell = 0;
    let buy = -prices[0];
    for (let i = 1; i < n; i++) {
        sell = Math.max(sell, buy + prices[i]);
        buy = Math.max(buy, -prices[i]);
    }
    return sell;
};

122. The best time to buy and sell stocks II (medium) unlimited number of transactions k = +infinity

State transition equation

//Not holding on day i is transferred from the maximum values of not holding and then not operating on day i-1 and holding and then selling on day i-1
dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])
//The holding on day i is transferred from the maximum values of holding and then not operating on day i-1 and not holding and then buying on day i-1
dp[i][k][1] = Math.max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])

k also does not affect the results. After simplification, it is as follows

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])

Complete code

const maxProfit = function (prices) {
    let n = prices.length;
    let dp = Array.from(new Array(n), () => new Array(2));
    dp[0][0] = 0; //Not held on day 0
    dp[0][1] = -prices[0]; //Price [0] spent buying on day 0
    for (let 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], dp[i - 1][0] - prices[i]);
    }
    return dp[n - 1][0];
};

State compression, also dp[i] is only related to dp[i - 1], remove one dimension

const maxProfit = function (prices) {
    let n = prices.length;
    let dp = Array.from(new Array(n), () => new Array(2));
    dp[0] = 0;
    dp[1] = -prices[0];
    for (let i = 1; i < n; i++) {
        dp[0] = Math.max(dp[0], dp[1] + prices[i]);
        dp[1] = Math.max(dp[1], dp[0] - prices[i]);
    }
    return dp[0];
};

//Semantics
const maxProfit = function (prices) {
    let n = prices.length;
    let sell = 0;
    let buy = -prices[0];
    for (let i = 1; i < n; i++) {
        sell = Math.max(sell, buy + prices[i]);
        buy = Math.max(buy, sell - prices[i]);
    }
    return sell;
};


123. The best time to buy and sell stocks III (hrad) limited number of transactions k=2

State transition equation

dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])
dp[i][k][1] = Math.max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])

k has an impact on the result and cannot be discarded. Only k can be cycled

for (let i = 0; i < n; i++) {
  for (let k = maxK; k >= 1; k--) {
    dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
    dp[i][k][1] = Math.max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);
  }
}


//k=2, write the result of the loop directly
dp[i][2][0] = Math.max(dp[i - 1][2][0], dp[i - 1][2][1] + prices[i])
dp[i][2][1] = Math.max(dp[i - 1][2][1], dp[i - 1][1][0] - prices[i])

dp[i][1][0] = Math.max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i])
dp[i][1][1] = Math.max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i])
            = Math.max(dp[i - 1][1][1], -prices[i])// When k=0, there are no transaction times, dp[i - 1][0][0] = 0

//Remove the dimension i
dp[2][0] = Math.max(dp[2][0], dp[2][1] + prices[i])
dp[2][1] = Math.max(dp[2][1], dp[1][0] - prices[i])

dp[1][0] = Math.max(dp[1][0], dp[1][1] + prices[i])
dp[1][1] = Math.max(dp[1][1], dp[0][0] - prices[i])
            = Math.max(dp[1][1], -prices[i])// When k=0, there are no transaction times, dp[i - 1][0][0] = 0

Complete code

//As before, we directly reduce the dimension
const maxProfit = function (prices) {
    let buy_1 = -prices[0], sell_1 = 0
    let buy_2 = -prices[0], sell_2 = 0
    let n = prices.length
    for (let i = 1; i < n; i++) {
        sell_2 = Math.max(sell_2, buy_2 + prices[i])
        buy_2 = Math.max(buy_2, sell_1 - prices[i])
        sell_1 = Math.max(sell_1, buy_1 + prices[i])
        buy_1 = Math.max(buy_1, -prices[i])
    }
    return sell_2
}

188. The best time to buy and sell stocks IV (hard) limit the number of transactions to k

const maxProfit = function (k, prices) {
    let n = prices.length;
    let profit = new Array(k);//Like question 123, find the states of all k
    // Profit from buying and selling of initial k transactions
    for (let j = 0; j <= k; j++) {
        profit[j] = {
            buy: -prices[0],//Indicates that there are shares
            sell: 0,//Indicates that there are no stocks
        };
    }
    for (let i = 0; i < n; i++) {
        for (let j = 1; j <= k; j++) {
            //Question 122 can be traded countless times and 188 can be traded k times, so you can add a layer of k cycle directly
          	//122 final recursive equation:
          	//sell = Math.max(sell, buy + prices[i]);
        		//buy = Math.max(buy, -prices[i]);
            profit[j] = {
                sell: Math.max(profit[j].sell, profit[j].buy + prices[i]),
                buy: Math.max(profit[j].buy, profit[j - 1].sell - prices[i]),
            };
        }
    }
    return profit[k].sell; //Returns the maximum profit after clearing the stock for the k-th time
};

309. The best time to buy and sell stocks includes the freezing period (medium) contains the transaction freeze period

State transition equation

dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])
//The cooling time is 1 day, so it is necessary to transfer from i - 2 days
//Buy, sell -- frozen period -- buy, sell
dp[i][k][1] = Math.max(dp[i - 1][k][1], dp[i - 2][k - 1][0] - prices[i])

The title does not limit the size of k and can be omitted

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 - 2][0] - prices[i])

//Dimension reduction i
dp[0] = Math.max(dp[0], dp[1] + prices[i])
dp[1] = Math.max(dp[1], profit_freeze - prices[i])

Complete code

const maxProfit = function (prices) {
    let n = prices.length;
    let buy = -prices[0];//Stock in hand
    let sell = 0;//No stock
    let profit_freeze = 0;
    for (let i = 1; i < n; i++) {
        let temp = sell;
        sell = Math.max(sell, buy + prices[i]);
        buy = Math.max(buy, profit_freeze - prices[i]);
        profit_freeze = temp;
    }
    return sell;
};

714. The best time to buy and sell stocks includes handling fees (medium) each transaction includes handling fee

State transition equation

//There is a handling charge for each transaction. We define that the handling charge is deducted when selling
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee)
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i])

Complete code

const maxProfit = function (prices, fee) {
    let sell = 0;//sell out
    let buy = -prices[0];//purchase
    for (let i = 1; i < prices.length; i++) {
        sell = Math.max(sell, buy + prices[i] - fee);
        buy = Math.max(buy, sell - prices[i]);
    }
    return sell;
};

322. Change (medium)

You can't use greed. For the counter example, coins=[1, 3, 5, 6, 7], amount=30. Use greed to use the largest denomination 7 first, and use two 1, 4 * 7 + 2 * 1 = 30. However, we can use five 6, 5 * 6 = 30 to exchange with the least coins

Method 1. Dynamic programming

  • Idea: DP [i] represents the minimum coins needed to exchange denomination I. because the coins are infinite, DP [i] can be calculated from bottom to top. For each state of dp[0~i], cycle the coins array to find a combination that can be exchanged, subtract the current coin value from the denomination I, and dp[i-coin] is DP [i] after adding a number of coins. Finally, the minimum value is the answer, and the state transition equation is DP [i] = Math.min(dp[i], dp[i-coin] + 1);
  • Complexity analysis: the time complexity is O(sn), s is the exchange amount, and N is the length of the coin array. A total of s states need to be calculated, and each state needs to traverse n denominations to transfer the state. The space complexity is O(s), that is, the length of the dp array

Js:

var coinChange = function (coins, amount) {
    let dp = new Array(amount + 1).fill(Infinity);//Initialize dp array
    dp[0] = 0;//Only 0 coins are needed for denomination 0

    for (let i = 1; i <= amount; i++) {//Circulating denomination
        for (let coin of coins) {//Circular coin array
            if (i - coin >= 0) {//When the denomination is greater than the value of the coin
                //dp[i - coin]: the minimum coin required for the current denomination i minus the current coin value
                //dp[i] can be converted from dp[i - coin] + 1
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
    }

    return dp[amount] === Infinity ? -1 : dp[amount];//If DP [amount] = = infinity, it cannot be redeemed
};

Java:

public class Solution {
    public int coinChange(int[] coins, int amount) {
        int max = amount + 1;
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, max);
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++) {
                if (coins[j] <= i) {
                    dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }
}

72. Edit distance (hard)

Method 1. Dynamic programming

  • Idea: dp[i][j] represents the minimum editing distance between the first I characters of word1 and the first j characters of word2.
    1. If word1 [I-1] = = word2 [J-1], it means that the last character does not need to be operated. At this time, dp[i][j] = dp[i-1][j-1], that is, the minimum operand at this time is the same as the minimum editing number of one character reduced by word1 and word2
    2. If word1 [I-1]== Word2 [J-1], there are three cases
      1. word1 deletes the last character, and the status changes to dp[i-1][j], that is, dp[i][j] = dp[i-1][j] + 1, + 1 refers to the deletion operation
      2. word1 adds a character at the end, and the status changes to dp[i][j-1], that is, dp[i][j] = dp[i][j-1] + 1, + 1 refers to the increase operation
      3. word1 replaces the last character, and the status changes to dp[i-1][j-1], that is, DP [i] [J] = dp[i-1][j-1] + 1, + 1 refers to the replacement operation
  • Complexity: the time complexity is O(mn), m is the length of word1, and N is the length of word2. The spatial complexity is O(mn), and a two-dimensional digital storage state with the size of m * n is required.

Js:

const minDistance = (word1, word2) => {
    let dp = Array.from(Array(word1.length + 1), () => Array(word2.length + 1).fill(0));

    //Initializing the array requires at least i operations for the first i characters of word1. For example, i deletes and becomes word2
    for (let i = 1; i <= word1.length; i++) {
        dp[i][0] = i;
    }

    //Initializing the array requires at least i operations for the first i characters of word2, such as j inserts into word1
    for (let j = 1; j <= word2.length; j++) {
        dp[0][j] = j;
    }

    for (let i = 1; i <= word1.length; i++) {
        //Loop word1 and word2
        for (let j = 1; j <= word2.length; j++) {
            if (word1[i - 1] === word2[j - 1]) {
                //If word1 [I-1] = = word2 [J-1], the last character does not need to be operated.
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                //dp[i-1][j] + 1: corresponding deletion
                //dp[i][j-1] + 1: add corresponding
                // dp[i-1][j-1] + 1: corresponding replacement operation
                dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + 1);
            }
        }
    }

    return dp[word1.length][word2.length];
};

Java:

public int minDistance(String word1, String word2) {
    int m = word1.length();
    int n = word2.length();
    int[][] dp = new int[m + 1][n + 1];

    for (int i = 1; i <= m; i++) {
        dp[i][0] =  i;
    }
    for (int j = 1; j <= n; j++) {
        dp[0][j] = j;
    }
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            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 - 1][j - 1], dp[i][j - 1]), dp[i - 1][j]) + 1;
            }
        }
    }
    return dp[m][n];
}

10. Regular expression matching(hard)

Method 1. Dynamic programming

  • Idea: dp[i][j] indicates whether the first I characters of s can match the first j characters of p. there are four cases. See the figure
  • Complexity: time complexity O(mn), m and N are the lengths of strings S and p respectively, and nested loops s and p are required. Space complexity O(mn), space occupied by dp array

js:

//dp[i][j] indicates whether the first I characters of s can match the first j characters of p
const isMatch = (s, p) => {
    if (s == null || p == null) return false;//In extreme cases, both s and p are null and return false

    const sLen = s.length, pLen = p.length;

    const dp = new Array(sLen + 1);//Because the position starts from 0 and the 0th position is an empty string, the initialization length is sLen + 1
    for (let i = 0; i < dp.length; i++) {//Initialize dp array
        dp[i] = new Array(pLen + 1).fill(false); // Default item to false
    }
    // base case s and p match at position 0
    dp[0][0] = true;
    for (let j = 1; j < pLen + 1; j++) {//Initialize the first column of dp. At this time, the position of s is 0
        //Case 1: if the j-1 position of p is *, the state of j is equal to the state of j-2
        //For example: S = 'p ='a *' is equivalent to looking forward 2 positions of P. if it matches, * is equivalent to repeating 0 characters
        if (p[j - 1] == "*") dp[0][j] = dp[0][j - 2];
    }
    // iteration
    for (let i = 1; i < sLen + 1; i++) {
        for (let j = 1; j < pLen + 1; j++) {

            //Case 2: if the current characters of s and p are equal or the current position of p is. Then the current dp[i][j] can be transferred from dp[i - 1][j - 1]
            //If the current position matches, both s and p look forward one bit. If all the previous characters match, all the characters in front of the current position also match
            //For example: S ='xxxa 'p ='xxx.' or S ='xxxa 'p ='xxxa'
            if (s[i - 1] == p[j - 1] || p[j - 1] == ".") {
                dp[i][j] = dp[i - 1][j - 1];
            } else if (p[j - 1] == "*") {//Case 3: enter the branch where the current character does not match. If the current p is *, it may match
                //If the current position of s is the same as the previous position of p or the previous position of p is equal to. There are three possibilities
                //If one of the conditions can be matched, the status of the current position can also be matched
                //dp[i][j - 2]: p look forward for 2 positions, which is equivalent to * repeating 0 times,
                //dp[i][j - 1]: p looks forward 1 position, which is equivalent to * repeating 1 time
                //dp[i - 1][j]: s looks forward one position, which is equivalent to * repeating n times
                //For example, s ='xxxa 'p ='xxxa *'
                if (s[i - 1] == p[j - 2] || p[j - 2] == ".") {
                    dp[i][j] = dp[i][j - 2] || dp[i][j - 1] || dp[i - 1][j];
                } else {
                    //Case 4: if the current position of s does not match the first two positions of p, it is equivalent to * repeated 0 times
                    //For example, the status of S ='xxxb 'p ='xxxa *' current position is the same as that of P looking forward
                    dp[i][j] = dp[i][j - 2];
                }
            }
        }
    }
    return dp[sLen][pLen]; // Does the s string with length sLen match the p string with length pnen
};


Java:

class Solution {
    public boolean isMatch(String s, String p) {
        if (p==null){
            if (s==null){
                return true;
            }else{
                return false;
            }
        }

        if (s==null && p.length()==1){
            return false;
        }

        int m = s.length()+1;
        int n = p.length()+1;

        boolean[][]dp = new boolean[m][n];

        dp[0][0] = true;

        for (int j=2;j<n;j++){
            if (p.charAt(j-1)=='*'){
                dp[0][j] = dp[0][j-2];
            }
        }

        for (int r=1;r<m;r++){
            int i = r-1;
            for (int c=1;c<n;c++){
                int j = c-1;
                if (s.charAt(i)==p.charAt(j) || p.charAt(j)=='.'){
                    dp[r][c] = dp[r-1][c-1];
                }else if (p.charAt(j)=='*'){
                    if (p.charAt(j-1)==s.charAt(i) || p.charAt(j-1)=='.'){
                        dp[r][c] = dp[r-1][c] || dp[r][c-2];
                    }else{
                        dp[r][c] = dp[r][c-2];
                    }
                }else{
                    dp[r][c] = false;
                }

            }
        }
        return dp[m-1][n-1];
    }
}

312. Poke the balloon (hard)

Method 1: dynamic programming

  • Idea: dp[i][j] represents the gold coins that can be obtained by opening the interval (i,j). K is the last balloon to be punctured in this interval. Enumerate I and j and traverse all intervals. The maximum number of gold coins that can be obtained by i-j is equal to the money obtained by puncturing the current balloon plus the gold coins already obtained in the previous i-k and k-j zones
  • Complexity: time complexity O(n^3), n is the number of balloons, three-level traversal. Space complexity O(n^2), dp array space.

js:

var maxCoins = function (nums) {
    const n = nums.length;
    let points = [1, ...nums, 1]; //Add virtual balloons on both sides
    const dp = Array.from(Array(n + 2), () => Array(n + 2).fill(0)); //dp array initialization
    //Bottom up transition state
    for (let i = n; i >= 0; i--) {
        //i decreasing
        for (let j = i + 1; j < n + 2; j++) {
            //j expanding
            for (let k = i + 1; k < j; k++) {
                //Enumerate all possible of k in i and j
                //The maximum amount of gold coins i-j can get is equal to the money obtained by puncturing the current balloon plus the gold coins obtained in the previous i-k and K-J intervals
                dp[i][j] = Math.max(
                    //Challenge maximum
                    dp[i][j],
                    dp[i][k] + dp[k][j] + points[j] * points[k] * points[i]
                );
            }
        }
    }
    return dp[0][n + 1];
};


java:

class Solution {
    public int maxCoins(int[] nums) {
        int n = nums.length;
        int[][] dp = new int[n + 2][n + 2];
        int[] val = new int[n + 2];
        val[0] = val[n + 1] = 1;
        for (int i = 1; i <= n; i++) {
            val[i] = nums[i - 1];
        }
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i + 2; j <= n + 1; j++) {
                for (int k = i + 1; k < j; k++) {
                    int sum = val[i] * val[k] * val[j];
                    sum += dp[i][k] + dp[k][j];
                    dp[i][j] = Math.max(dp[i][j], sum);
                }
            }
        }
        return dp[0][n + 1];
    }
}

343. Integer splitting (medium)

  • Idea: dp[i] is the maximum product of the positive integer I after splitting, and the cyclic number n. split each number and take the maximum product. The state transition equation: dp[i] = Math.max(dp[i], dp[i - j] * j, (i - j) * j), j*(i-j) means to split I into J and i-j, and j * dp[i-j] means to split I into J and continue to split (i-j) and take (i-j) The maximum product in the split result is multiplied by J
  • Complexity: time complexity O(n^2), two-level cycle. Space complexity O(n), dp array space

js:

var integerBreak = function (n) {
    //dp[i] is the maximum product of positive integer I after splitting
    let dp = new Array(n + 1).fill(0);
    dp[2] = 1;

    for (let i = 3; i <= n; i++) {
        for (let j = 1; j < i; j++) {
            //j*(i-j) means dividing i into j and i-j and multiplying them
            //j*dp[i-j] means splitting i into j and continuing to split the number (i-j), taking the maximum product of (i-j) splitting result and multiplying it by j
            dp[i] = Math.max(dp[i], dp[i - j] * j, (i - j) * j);
        }
    }
    return dp[n];
};


java:

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n+1]; 
        dp[2] = 1;//Initial state
        for (int i = 3; i <= n; ++i) {
            for (int j = 1; j < i - 1; ++j) {
                dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
            }
        }
        return dp[n];
    }
}

0-1 knapsack problem

0-1 knapsack problem refers to the knapsack with n items and capacity of j. the weight array records the weight of n items, the weight of the item at position I is weight[i], the value array records the value of n items, and the value of the item at position I is values [i]. Each item can only be put into the knapsack once. Ask to load those items into the knapsack to maximize the value of the knapsack.

give an example:

We do it by dynamic programming

  • Status definition: dp[i][j] refers to the maximum value of taking any item from the previous I and putting it into the backpack with capacity j

  • State transition equation: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); Each item can be put into the backpack or not

    1. When j - weight [i] < 0: it means that no I element can be loaded and will not be put into the backpack. At this time, dp[i][j] = dp[i - 1][j], dp[i] [j] depends on the maximum value of the items in the first i-1 loaded into the backpack with capacity j
    2. When J - weight [i] > = 0: you can choose to put it in or not put it in the backpack.
      Put it into the backpack: dp[i][j] = DP [I - 1] [j-weight[i]] + value[i], DP [I - 1] [j-weight[i]] represents the maximum value of the items in i-1 loaded into the backpack with the capacity of j-weight[i], and then add the value[i] of the items to transfer the state to dp[i][j].
      If it is not put into the backpack, dp[i][j] = dp[i - 1] [j], whichever is greater.
  • Initialize dp array: dp[i][0] indicates that the volume of the backpack is 0, then the value of the backpack must be 0, and dp[0][j] indicates the value of the backpack after item 0 is put into the backpack

  • Finally, you need to return the value: the last column of the last row of the dp array

The dp array after the loop is completed is shown in the following figure

js:

function testWeightBagProblem(wight, value, size) {
    const len = wight.length,
        dp = Array.from({ length: len + 1 }).map(//Initialize dp array
            () => Array(size + 1).fill(0)
        );
    //Note that we let i start with 1, because we sometimes use i - 1 to prevent the array from going out of bounds
    //Therefore, when initializing the dp array, the length is wight.length+1
    for (let i = 1; i <= len; i++) {
        for (let j = 0; j <= size; j++) {
            //Because the length of weight is weight. Length + 1, and the subscript of the item starts from 1, i should be reduced by 1
            if (wight[i - 1] <= j) {
                dp[i][j] = Math.max(
                    dp[i - 1][j],
                    value[i - 1] + dp[i - 1][j - wight[i - 1]]
                )
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }

    return dp[len][size];
}

function test() {
    console.log(testWeightBagProblem([1, 3, 4], [15, 20, 30], 4));
}

test();

State compression

According to the state transition equation dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]), line I is only related to the state of line i-1, so we can use the rolling array for state compression. Secondly, we note that j is only related to the state in front of J, so we can use only one array to calculate the state from back to front.

The animation is too large. Click to view it

function testWeightBagProblem2(wight, value, size) {
    const len = wight.length,
        dp = Array(size + 1).fill(0);
    for (let i = 1; i <= len; i++) {
        //It is calculated from back to front. If it is from front to back, the latest value will overwrite the old value, resulting in incorrect calculation results
      	//dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - wight[i - 1]] + value[i - 1])
        for (let j = size; j >= wight[i - 1]; j--) {
            dp[j] = Math.max(dp[j], dp[j - wight[i - 1]] + value[i - 1] );
        }
    }
    return dp[size];
}

416. Segmentation and subsets (medium)

  • Idea: this problem can be regarded as a 0-1 knapsack problem. Give a knapsack with a loadable weight of sum / 2 and N items. The weight of each item is recorded in the num array. Ask whether the knapsack can be filled exactly in one loading method? dp[i][j] indicates whether the first I items can fill the backpack with volume j. when dp[i][j] is true, it indicates that it can be filled exactly. Each number can be put into the backpack or not. The analysis method is the same as that of the 0-1 backpack problem.
  • Complexity: time complexity O(n*sum), n is the length of num array, and sum is the sum of num array elements. The space complexity is O(n*sum), and the state compression is followed by O(sum)

js:

//It can be regarded as a 0-1 knapsack problem. Give a knapsack with a loadable weight of sum / 2 and N items,
//The weight of each item is recorded in the num array. Is it possible to fill the backpack exactly in one loading method?
var canPartition = function (nums) {
    let sum = 0
    let n = nums.length
    for (let i = 0; i < n; i++) {
        sum += nums[i]
    }
    if (sum % 2 !== 0) {//If it is an odd number, it cannot be divided and returns false directly
        return false
    }
    sum = sum / 2
    //dp[i][j] indicates whether the first I items can fill the backpack with volume j. when dp[i][j] is true, it indicates that it can be filled exactly
    //Finally, dp[n][sum] indicates whether the first n items can just fill the backpack with sum capacity
    //The length of dp array is n+1, and it is a two-dimensional array. The first dimension represents the index of items, and the second dimension represents the backpack size
    let dp = new Array(n + 1).fill(0).map(() => new Array(sum + 1).fill(false))
    //dp array initialization, dp[..][0] = true indicates that the backpack capacity is 0, and it is full at this time,
    //dp[0][..] = false indicates that there are no items. It must be filled with insufficient items
    for (let i = 0; i <= n; i++) {
        dp[i][0] = true
    }
    for (let i = 1; i <= n; i++) {//i starts traversing from 1 to prevent the array from crossing the boundary when taking dp[i - 1][j]
        let num = nums[i - 1]
        //J starts from 1 and j is 0, which has been completed during the initialization of dp array
        for (let j = 1; j <= sum; j++) {
            if (j - num < 0) {//The capacity of the backpack is insufficient and cannot be put into the backpack
                dp[i][j] = dp[i - 1][j];//dp[i][j] depends on whether the first i-1 items can fill the capacity of j
            } else {
                //dp[i - 1][j] indicates that the ith item is not loaded
                //dp[i - 1][j-num] indicates loading the ith. At this time, you need to look forward to see if the front i - 1 can be filled with j-num
                //The difference between backpack and backpack is that it only returns true and false to indicate whether it can be filled, without calculating the value
                dp[i][j] = dp[i - 1][j] || dp[i - 1][j - num];
            }
        }
    }
    return dp[n][sum]
};

//State transition equation f [I, target] = f [I - 1, target] | f [I - 1, target - nums [i]]
//The state of line n depends only on the state of line n-1
//State compression
var canPartition = function (nums) {
    let sum = nums.reduce((acc, num) => acc + num, 0);
    if (sum % 2) {
        return false;
    }
    sum = sum / 2;
    const dp = Array.from({ length: sum + 1 }).fill(false);
    dp[0] = true;

    for (let i = 1; i <= nums.length; i++) {
        //It is calculated from back to front. If it is from front to back, the latest value will overwrite the old value, resulting in incorrect calculation results
        for (let j = sum; j > 0; j--) {
            dp[j] = dp[j] || (j - nums[i] >= 0 && dp[j - nums[i]]);
        }
    }

    return dp[sum];
};

java:

public class Solution {

    public boolean canPartition(int[] nums) {
        int len = nums.length;
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if ((sum & 1) == 1) {
            return false;
        }

        int target = sum / 2;
        boolean[][] dp = new boolean[len][target + 1];
        
        dp[0][0] = true;

        if (nums[0] <= target) {
            dp[0][nums[0]] = true;
        }
        for (int i = 1; i < len; i++) {
            for (int j = 0; j <= target; j++) {
                dp[i][j] = dp[i - 1][j];
                if (nums[i] <= j) {
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
                }
            }

            if (dp[i][target]) {
                return true;
            }
        }
        return dp[len - 1][target];
    }
}

//State compression
public class Solution {

    public boolean canPartition(int[] nums) {
        int len = nums.length;
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if ((sum & 1) == 1) {
            return false;
        }

        int target = sum / 2;
        boolean[] dp = new boolean[target + 1];
        dp[0] = true;

        if (nums[0] <= target) {
            dp[nums[0]] = true;
        }
        for (int i = 1; i < len; i++) {
            for (int j = target; nums[i] <= j; j--) {
                if (dp[target]) {
                    return true;
                }
                dp[j] = dp[j] || dp[j - nums[i]];
            }
        }
        return dp[target];
    }
}

198. House raiding (medium)

  • Idea: dp[i] represents the maximum amount that 0-i can steal, and dp[i] is transferred from the maximum value in the two cases
    1. dp[i-2] + nums [i] means stealing the current location, so the location of i-1 cannot be stolen, and dp[i-2] needs to be added, that is, the money of the first i-2 rooms
    2. dp[i - 1] means stealing the current location, only i-1 rooms
  • Complexity: time complexity O(n), traversing the array once, space complexity O(1), O(1) after state compression, O(n) without state compression

js:

//dp[i] represents the maximum amount that 0-i can steal
const rob = (nums) => {
    const len = nums.length;
    const dp = [nums[0], Math.max(nums[0], nums[1])]; //Initializes the first two items of the dp array
    for (let i = 2; i < len; i++) {
        //Traverse from the third position
        //dp[i - 2] + nums[i] means stealing the current location, so the location of i-1 cannot be stolen,
      	//And you need to add dp[i-2], that is, the money for the first i-2 rooms
        //dp[i - 1] means stealing the current location, only i-1 rooms
        dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
    }
    return dp[len - 1]; //Returns the last largest item
};

//State compression
var rob = function (nums) {
    if(nums.length === 1) return nums[0]
    let len = nums.length;
    let dp_0 = nums[0],
        dp_1 = Math.max(nums[0], nums[1]);
    let dp_max = dp_1;
    for (let i = 2; i < len; i++) {
        dp_max = Math.max(
            dp_1, //Don't rob the current home
            dp_0 + nums[i] //Rob the current home
        );
        dp_0 = dp_1; //Rolling exchange variable
        dp_1 = dp_max;
    }
    return dp_max;
};

java:

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

//State compression
class Solution {
    public int rob(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int len = nums.length;
        int dp_0 = 0,
            dp_1 = nums[0];
        int dp_max = nums[0];
        for (int i = 2; i <= len; i++) {
            dp_max = Math.max(
                dp_1, //Don't rob the current home
                dp_0 + nums[i - 1] //Rob the current home
            );
            dp_0 = dp_1; //Rolling exchange variable
            dp_1 = dp_max;
        }
        return dp_max;
    }
}

64. Minimum path and (medium)

  • Idea: dp[i][j] represents the minimum path sum from the upper left corner of the matrix to the grid (I, j). As long as the grid is traversed from top to bottom and from left to right, the current minimum path sum is the current value plus the upper and left small ones.
  • Complexity: time complexity O(mn), m and n are the length and width of the matrix respectively. If the space complexity is modified in place, it is O(1), and if the new dp array is created, it is O(mn)

js:

var minPathSum = function(dp) {
    let row = dp.length, col = dp[0].length

    for(let i = 1; i < row; i++)//Initialize the first column
        dp[i][0] += dp[i - 1][0]

    for(let j = 1; j < col; j++)//Initialize the first line
        dp[0][j] += dp[0][j - 1]

    for(let i = 1; i < row; i++)
        for(let j = 1; j < col; j++)
            dp[i][j] += Math.min(dp[i - 1][j], dp[i][j - 1])//Take the smallest one on the top and left
    
    return dp[row - 1][col - 1]
};

java:

class Solution {
    public int minPathSum(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        int rows = grid.length, columns = grid[0].length;
        int[][] dp = new int[rows][columns];
        dp[0][0] = grid[0][0];
        for (int i = 1; i < rows; i++) {
            dp[i][0] = dp[i - 1][0] + grid[i][0];
        }
        for (int j = 1; j < columns; j++) {
            dp[0][j] = dp[0][j - 1] + grid[0][j];
        }
        for (int i = 1; i < rows; i++) {
            for (int j = 1; j < columns; j++) {
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        return dp[rows - 1][columns - 1];
    }
}

Posted by GetReady on Sun, 21 Nov 2021 23:42:37 -0800