Dynamic programming - classification summary

Dynamic programming (DP) idea

Dynamic programming = > DP

Dynamic programming: ordinary recursion + dp array records to achieve space for time

Dynamic programming is generally not very efficient because dp[] it records the optimal solution of each state on the way

Try to find clever methods to achieve better solutions than dp

Three properties of DP:

  • Optimal substructure
  • Subproblem overlap
  • No aftereffect (calculated dp[] will not be changed)

DP four steps:

  1. Split sub problems
  2. Recurrence formula of subproblem (state transition equation)
  3. Determine the calculation order of DP array and initialize it
  4. Space optimization (optional)

Dynamic programming vs greedy algorithm

Greedy algorithm is a special dynamic programming algorithm

For a dynamic programming problem, the optimal solution of the problem often includes the optimal solution of repeated subproblems. Dynamic programming is to eliminate repeated subproblems

The greedy algorithm is greedy to select a subproblem every time, so it will not repeatedly calculate the optimal solution of the subproblem

General classification of DP problems

  • [fiborache] (step jumping Series)
  • [recursive type] (ugly number, rope cutting, last number of circles)
  • [classification] discontinuous sequence - maximum value (looting, stock, discontinuous subsequence)
  • [two dimensional coordinate type] is subdivided into: 1) checkerboard dfs backtracking problem (flag trial and error) 2) checkerboard dp [] [] recurrence problem
  • [interval type] continuous sequence - maximum value (taking stones and continuous subsequences) = "two-dimensional dp [] [], double pointer
  • [knapsack type] target value (knapsack (sum < = k), sequence with sum K (not necessarily continuous), change exchange)
  • [tree type] (tree recursion, dp; it is often drawn to the tree instead of dynamic programming)

The first three types are one-dimensional dp [], followed by two-dimensional dp [] [], and the knapsack type may be one-dimensional, two-dimensional and multi-dimensional

Fiborache type

1. Fibonacci sequence

We all know the Fibonacci sequence. Now it is required to input an integer n. please output the nth item of the Fibonacci sequence (starting from 0, item 0 is 0, and item 1 is 1). n≤39

public class Solution {
    public int Fibonacci(int n) {
        int[] fi=new int[40];//Set the array to record the intermediate results, otherwise there will be too many repeated calculations. / / set the array size according to the topic	 
        fi[0]=0;fi[1]=1;
        for(int i=2;i<=n;i++){
            fi[i]=fi[i-1]+fi[i-2];
        }
        return fi[n];
    }
}
//Dynamic programming, time complexity O(N), space complexity O(N)
//If recursion is used, the time complexity is O(1.618^N) [slightly less than 2^N for online search], and the space complexity is O(1) [excluding system stack space]

2. Step jumping

A frog can jump up one step or two steps at a time. Find out the total number of jumping methods for the frog to jump up an n-step step (different results are calculated in different order).

1) Fiborache-O(N) dynamic programming

public class Solution {
    public int JumpFloor(int target) {
        int frog[]=new int[100];
        frog[1]=1;frog[2]=2;
        for (int i=3;i<=target;i++){
            frog[i]=frog[i-1]+frog[i-2];
        }
        return frog[target];
    }
}
//The principle is the same as that of Fibonacci sequence
//[dynamic programming] time O(N), space O(N)
//If you only need the final result, you can undo the array and use three variables a/b/c to store it. Space complexity reduced to O(1)

2) Method of space O(1)

public class Solution {
    public int jumpFloor(int target) {
        if(target<=2)return target;
        int lastOne = 2;  //The current position is one, which is equivalent to fi[i-1]
        int lastTwo = 1;  //Equivalent to fi[i-2]
        int res = 0;
        for(int i=3; i<=target; ++i){
            res = lastOne + lastTwo;
            lastTwo = lastOne;
            lastOne = res;
        }
        return res;
    }
}
//The spatial complexity of this method is O(1)
//Although the time complexity is also O(N), it is more time-consuming than the previous dynamic programming method because there are more operations in the loop
//Equivalent to time for space, spend time in constantly tossing places

3. Step jump expansion problem

A frog can jump up 1 step or 2 steps at a time... It can also jump up n steps. Find out how many jumping methods the frog can jump up an n-step.
1) Find the formula

public class Solution {
    public int JumpFloorII(int target) {
        int way=1;for(int i=1;i<target;i++)way*=2;return way;
    }
}
//[find out the mathematical formula] n-1 power of 2: draw a horizontal line to n-1 spaces between n points
// In fact, it's not difficult to find. When looking for recursive formulas, you know the first few items as soon as you write them
// Time O(N)
// Space O(1)

2) (dynamic programming) hard calculation

public class Solution {
    public int jumpFloorII(int target) {
        int[] array =new int[100];
        array[1] = 1;
        for(int i=2; i<=target; ++i){
            int sum = 0;
            for(int j=1; j<=i-1; ++j)sum+=array[j];
            array[i] = sum +1;  //All previous paths, plus one hop directly
        }
        return array[target];
    }
}
//Time O(N^2)
//Space o

4. Rectangular coverage

We can use the small rectangle of 21 to cover the larger rectangle horizontally or vertically. How many methods are there to cover a 2n large rectangle with n 21 small rectangles without overlapping?
For example, when n=3, the rectangular block of 23 has three covering methods:

public class Solution {
    public int rectCover(int target) {
        int fi[] = new int[100];
        for(int i= 0; i<=2; ++i)fi[i]=i;
        for(int i=3; i<=target; ++i)fi[i]=fi[i-1]+fi[i-2];
        return fi[target];
    }
}
//(except for a little difference at the beginning, fiboracci is followed)
// Finding recurrence relationship: decomposition = = "the rightmost case can only be vertical or horizontal. These two cases have no intersection and occupy 1 block and 2 blocks respectively

Recursive type

1. Ugly number

N umbers containing only qualitative factors 2, 3 and 5 are called ugly numbers. For example, 6 and 8 are ugly numbers, but 14 is not because it contains quality factor 7. Traditionally, we regard 1 as the first Ugly Number. Find the nth Ugly Number in the order from small to large.

import java.lang.Math;

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        int ugly [] = new int [2000];
        ugly[1] = 1;//The first ugly number is the 1 / / ugly [] array: start with 1 instead of 0 to increase readability
        int t2 = 1;
        int t3 = 1;
        int t5 = 1;//In the three tracks marked 2 / 3 / 5 (non independent), the position of potential candidates / / ugly [] subscript / / t2t3t5 runs slower than i
        for(int i=2; i<=index; ++i){
            ugly[i] = Math.min(Math.min(2*ugly[t2],3*ugly[t3]),5*ugly[t5]);//The min() in Java is too low. It can only be two numbers
            if(ugly[i] == 2*ugly[t2]) ++t2;//t2 go along the trunk line ugly[] to the next one: because this one is selected, the next one is selected as a candidate
            if(ugly[i] == 3*ugly[t3]) ++t3;
            if(ugly[i] == 5*ugly[t5]) ++t5;//Why three similar statements? Because: there may be one, two or all of these three factors (all three factors contain)
        }
        return ugly[index];
    }
}
//Time O(N) space O(N)

2. Cut the rope

Here is a rope with length n. please cut the rope into m segments with integer length (M and N are integers, n > 1 and M > 1). The length of each segment of rope is recorded as k[0],k[1],...,k[m]. What is the possible maximum product of k[0]xk[1]x...xk[m]? For example, when the length of the rope is 8, we cut it into three segments with lengths of 2, 3 and 3 respectively. At this time, the maximum product is 18. (2 <= n <= 60)

Method 1: derivation of mathematical function

[general idea] take the derivation of the constructor and get: m=n/e (in the case of decimal), that is, try to split it into a pile: 2 and 3 (the integer closest to e)

Derivation of mathematical function: targeted law
result= f(m) = (n/m) ^m, set n as the fixed value, m as the independent variable, and f(m) as the product result.
Max {f (m)} = max {ln f (m)}, take logarithm.
Find the m value of the maximum point of LN f (m) = m * (LN n - ln m), find the derivative and let f(m)'=0 to obtain m=n/e
e=2.718, and then it is disassembled into a pile of 2 and 3 because it takes an integer;
See below for details: 4 > > > 2x2; 5>>>2x3; 6> > > 3x3 meets the results of the analysis.

public class Solution {
    public int cutRope(int target) {
        if(target == 2)return 1;//Because the title requires at least two copies
        if(target == 3)return 2;
        int res = 1;
        while(target > 4 ){//When target remains < = 4, there are three cases: 4 = > 2 * 2 = 4; 3=>3;  2=>2;  (- = 3 does not exist 1)
            target -= 3;
            res *= 3;
        }
        return res * target;//Three cases are combined
    }
}//Time O(N), space O(1)

Method 2: dynamic programming

[general idea] dp[] save the best step by step + find the recurrence formula

public class Solution {
    int[] dp = new int[60];
    public int cutRope(int target) {
        if(target == 2) return 1;
        if(target == 3) return 2;//The strategy here is different. We should carry it out alone
        dp[2] = 2;
        dp[3] = 3;//On the premise of target > = 4, dp [] the corresponding values of array 2 ~ 3 (there is no need to force two segments)
        for(int i=4; i<=target; ++i){
            int max = Integer.MIN_VALUE;
            for(int j=2; j<=i-1; ++j){//Sure enough, the essence of dp is exhaustion
                if(max < dp[j]*(i-j)) max = dp[j]*(i-j);//Dynamic programming focuses on finding = > [recursive formula of optimal substructure]
            }//Another recursion: replace the (i-j) of the previous line with dp[i-j]
            dp[i] = max;
        }
        return dp[target];
    }
}//Time O(N^2) space O(N)

3. Children's games (the last number left in the circle)

Every year, children's day will be arranged to prepare some small gifts for children in orphanages. This year is also true. As a senior veteran of Niuke, HF has naturally prepared some games. Among them, there is a game like this: first, let the children form a big circle. Then, he randomly assigned a number m and asked the child with number 0 to start counting. Every time the child who cries out to M-1 wants to line up and sing a song, then he can choose any gift in the gift box and don't go back to the circle. Starting from his next child, he continues to count 0...m-1.... this goes on... Until the last child is left, he doesn't have to perform and gets the famous detective Conan Collection Edition of Niuke (the quota is limited!!). Please try to think which child will get this gift? (Note: Children's numbers are from 0 to n-1; number 0 to m-1)

If there are no children, please return - 1

Method 1: naive simulation method O(m*n)

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n<=0 || m<=0)return -1;
        ListNode head = new ListNode(0);
        ListNode p = head;
        for(int i=1; i<=n-1; ++i){
            ListNode node = new ListNode(i);//String into a chain
            p.next = node;
            p = p.next;
        }
        p.next = head;//p returns to the previous position at the beginning to form a closed loop
                      //Another function is to make p point to the beginning before the head, which can make each cycle the same routine
        for(int i=1; i<=n-1; ++i){
            for(int j=1; j<=m-1; ++j){
                p=p.next;
            }
            p.next = p.next.next;//java will recycle automatically, so no matter which node is deleted
        }
        return p.val;//The last one left
    }
}//This idea is: simulate the complete game operation mechanism without jumping steps
//Time O(m*n) space O(n)

Method 2: mathematical induction O(n) [analysis is difficult]

public class Solution {
    public int LastRemaining_Solution(int n, int m) {//Time reversal recursive method: why reverse? Because the reverse is from less to more, there will be no vacancy; There will be vacancies in the forward direction, which must be simulated and can not skip steps
        if(n<=0)return -1;
        int res = 0; //f(1,m)=0
        for(int i=2; i<=n; i++){//i is the number of children. i=2 is the last round of the game, but the first round of my solution / / the cycle goes from i=2 to i=n. there are more and more children. This solution is backward (time goes back)
            res = (res + m) % i;  //Adjacency relation: res on the left is f(n,m) and res on the right is f(n-1,m)
        }
        return res;
    }
}//Time O(n) space O(1)

Mathematical induction analysis:

Mathematical induction:
f(n,m) means: [relative reference system: start from position 0 and finally reach position f(n,m)] / / for example, start from 0 and finally reach position f(5,3)=3
f(1,m)=0;// First item
f(n,m)=[(m%n) + f(n-1,m)]%n;// The formula is simplified as: f(n,m)=[m + f(n-1,m)]%n / / adjacent term relationship [key points, derivation is as follows]:
For example:
F (5,3) = [f (4,3) + 3% 5]% 5 = f (4,3) + 3 what do you mean?
f(5,3) starts from 0, 0-1-2, deletes 2 nodes, and then comes to 3 = =. At this time, the situation is similar to f(4,3). But there is another difference, that is, the standard f(4,3) starts from 0, and here starts from 3
f(4,3) according to the definition, it must start from 0 (all definitions of f(i,m) need to be consistent), not from 3. Therefore, you must perform [alignment]:
Therefore, in the reference system of f(5,3): take 3 steps first, and then take 3 as the starting point, take f(4,3) steps = = "f(5,3)=3+ f(4,3) [here is simplification, and then consider the details of% n. It's ok under optimization]
With the recursive formula, the solution is almost the same with the recursive method or iterative method
Iterative method: F (1, m) = 0, f (2, m) = [M + F (1, m)]% n... Until f(n,m)

Division type

1. The best time to buy and sell stocks III

Given an array, its ith element is the price of a given stock on day i.

Design an algorithm to calculate the maximum profit you can make. You can complete up to two transactions.

Note: you cannot participate in multiple transactions at the same time (you must sell the previous shares before buying again).

class Solution {
    public int maxProfit(int[] prices) {
    	//There are few states in this question, only one dimension and only four states
    	//These four variables are the total profit in the current state:
        int buy1 = prices[0];
        int buy2 = prices[0];
        int sell1 = 0;
        int sell2 = 0;
        for(int i=0; i<=prices.length-1; ++i){
        	//The following four lines are state transition equations:
            buy1 = Math.min(buy1, prices[i]);
            sell1 = Math.max(sell1, prices[i]-buy1);
            buy2 = Math.min(buy2, prices[i]-sell1);
            sell2 = Math.max(sell2, prices[i]-buy2);
        }
        return sell2;
    }
}//Time O(N) space O(1)

Similar topic 1
Title: there is only one trading opportunity
Solution: just leave buy1 and sell1 in the above method

Similar topic 2
Title: k times of trading
Solution: (difficult) add array and layer loop in the above method

2. The best time to buy and sell stocks includes handling fees

You only need to pay a handling fee for each transaction.

Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
Output: 8

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int profit =0;
        int buy = prices[0] + fee;
        for(int i = 0; i<=prices.length-1; ++i){
            if(buy > prices[i]+ fee)buy = prices[i]+ fee;//Update lowest price
            if(prices[i] > buy){
                profit += (prices[i] - buy);
                buy = prices[i];//Here's the key: when we sell a stock, we immediately get the right to buy a stock at the same price without handling charges
            }
        }
        return profit;
    }
}//Time O(N) space O(1)

Similar topics
Title: unlimited trading times
Solution: operate every step and accept all "uphill"

3. Looting (non adjacent maximum subsequence sum)

Given a nonnegative integer array representing the storage amount of each house, the maximum amount that can be stolen overnight without stealing two adjacent houses.

Key points:

State transition equation: DP [i] = DP [I-1] > DP [I-2] + nums [I-1]? dp[i-1] : dp[i-2]+nums[i-1];

Note that the analysis of dp[i] only needs the relationship with dp[i-1] and dp[i-2]

class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        int dp[] = new int[len + 1];//advantage
        dp[0] = 0;
        dp[1] = nums[0];
        for(int i = 2; i<=len; ++i){
            dp[i] = dp[i-1] > dp[i-2]+nums[i-1] ? dp[i-1] : dp[i-2]+nums[i-1];
        }
        return dp[len];
    }
}//Time O(N) space O(N)

Space optimization:

class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        int two = 0;
        int one = nums[0];
        int zero = nums[0];
        for(int i = 2; i<=len; ++i){
            zero = one > two+nums[i-1] ? one : two+nums[i-1];//State transition equation
            two = one;
            one = zero;
        }
        return zero;
    }
}//Time O(N) space O(1)
//Although this time is also O(N), it is more time-consuming than the above method (time for space)

4. Longest increasing subsequence

Method 1: double cycle complete dp

Description:
One dimensional array dp[i] is used to store the maximum res of 0-i sequence, which is initialized to 1
i. j double cycle, when num [j] < num [i]:
dp[i] = Math.max(dp[i], dp[j] + 1);
Time O(N^2) space O(N)

Method 2: set auxiliary array
Time O(N*logN) space O(N)

import java.util.ArrayList;

class Solution {
    public int lengthOfLIS(int[] nums) {
        ArrayList<Integer> min = new ArrayList<Integer>();//The tails may not be the value of the subsequence, but the length must be correct
        min.add(nums[0]);
        for(int num:nums){
            int left = 0;
            int right = min.size()-1;
            if(num > min.get(right))
                min.add(num);
            else{
                while(left < right){
                    int mid = (left+right)/2;
                    if(num <= min.get(mid)) right = mid;//There are many ways to write while here. As long as num and min[mid] are equal, you can turn left between time zones
                    else left = mid+1;
                }
                min.set(right,num);//left==right / / the latest number must be inserted into the queue for each round
            }
        }
        return min.size();
    }
}

How to make the code shorter:

public class Solution {
    public int LIS(int[] arr) {
        int[] min = new int[arr.length];
        int count = 0;
        for(int num:arr){
            int left = 0;
            int right = count;//Right here contains the right one of the valid values of the min array
            while(left<right){
                int mid = (left+right)/2;
                if(num>min[mid])left = mid+1;
                else right = mid;
            }
            if(count == right) ++count;
            min[right]=num;
        }
        return count;
    }
}

Deepening: it is required to output the optimal sequence (when the length is the same, each position is required to be as small as possible)

public class Solution {
    public int[] LIS(int[] arr) {
        int n = arr.length;
        int[] min = new int[n];
        int[] index = new int[n];//For each arr element, the subscript of the record (in min) is updated
        int count = 0;
        for(int i=0; i<=n-1; ++i){
            int left = 0;
            int right = count;
            while(left<right){
                int mid = (left+right)/2;
                if(arr[i]>min[mid])left = mid+1;
                else right = mid;
            }
            if(count == right) ++count;
            min[right]=arr[i];
            index[i] = right;//Update subscript right in record (in min [])
        }
        int[] res = new int[count];//
        for(int i= n-1,k = count-1; i>=0; --i){//
            if(k == index[i])res[k--]=arr[i];//You must go from right to left to get the latest
        }
        return res;
    }
}

Two dimensional dp[][] recurrence type

1. Different paths

Method 1: dynamic programming

Key points:

1) State transition equation: path [i] [J] = path [I-1] [J] + path [i] [J-1];

2) Initialization: the first row and the first column are initialized to 1 (because there is only one method)

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] path = new int [m][n];
        //The uppermost and leftmost edges are initialized to 1 (because there is only one way to reach them)
        for(int i=0; i<=m-1; ++i)path[i][0]=1;
        for(int j=0; j<=n-1; ++j)path[0][j]=1;
        for(int i=1; i<=m-1; ++i){
            for(int j=1; j<=n-1; ++j){
                path[i][j]=path[i-1][j]+path[i][j-1];
            }
        }
        return path[m-1][n-1];
    }
}

Time O(mn), space O(mn)

Method 2: Combinatorial Mathematics
M x N grid, top left to bottom right
Analysis: a total of (M-1) + (N-1) steps, including (M-1) steps to the right, so it is:

Time O(n), space O(1)

2. Minimum path and

Given an m x n grid containing non negative integers, please find a path from the upper left corner to the lower right corner so that the sum of numbers on the path is the smallest.

public class Solution {
    public int minPathSum(int[][] matrix) {
        int row = matrix.length;
        int col = matrix[0].length;
        int[][]sum = new int[row][col];
        int s = 0;
        for(int i=0; i<=row-1; ++i){
            s += matrix[i][0];
            sum[i][0] = s;
        }
        s = 0;
        for(int j=0; j<=col-1; ++j){
            s += matrix[0][j];
            sum[0][j] = s;
        }
        for(int i=1; i<=row-1; ++i){
            for(int j=1; j<=col-1; ++j){
                sum[i][j] = sum[i-1][j]<sum[i][j-1] ? sum[i-1][j]+matrix[i][j] : sum[i][j-1]+matrix[i][j];
            }
        }
        return sum[row-1][col-1];
    }
}//Time O(mn), space O(mn)

Optimize Tips:

1) You can directly modify the grid array provided by the topic, so that the space is O(1).

2) If the sum[row+1][col+1] array is used and the virtual edge is 0, the two loops with unified steps and no initialization can be used.

Similar topics
Topic: Triangle minimum path and
Solution:
Sets an auxiliary dp array with a minimum sum
Step 1: the left boundary and the right boundary have only one "parent"; Calculate the edge first
Step 2: the inner points come from the min of the two "parents"
Step 3: take the minimum value of the lowest layer
(Space Optimization: only keep the previous row of calculation)

3. Maximum square

On a two-dimensional chessboard, all values are 0 or 1. The largest square whose completeness is 1.

public class Solution {
    public int maximalSquare(char[][] matrix) {//Dynamic programming O(N^2) / / violence method O(N^4)
        int maxSide = 0;
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return maxSide;
        }
        int rows = matrix.length;
        int columns = matrix[0].length;
        int[][] dp = new int[rows][columns];
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < columns; ++j) {
                if (matrix[i][j] == '1') {//The lower right corner is not empty
                    if (i == 0 || j == 0) {
                        dp[i][j] = 1;
                    } else {
                        dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;//This sentence is the core / / the smallest + 1 of the three
                    }
                    maxSide = Math.max(maxSide, dp[i][j]);
                }
            }
        }
        int maxSquare = maxSide * maxSide;
        return maxSquare;
    }
}

Two dimensional dfs backtracking type (flag trial and error)

1. Path in matrix

Please design a function to judge whether there is a path containing all characters of a string in a matrix. The path can start from any grid in the matrix. Each step can move one grid left, right, up and down in the matrix. If a path passes through a lattice in the matrix, the path cannot enter the lattice again. For example:

The matrix contains a path of the string "bcced", but the matrix does not contain a path of "abcb", because the first character b of the string occupies the first row and the second grid in the matrix. After the second grid, the path cannot enter the grid again.

[general idea] (dfs + pruning) x multiple starting points

public class Solution {
    public boolean hasPath (char[][] matrix, String word) {
        boolean flag[][] = new boolean[matrix.length][matrix[0].length];//flag [] [] array, initialized to false, indicating points that have not passed
        //After one initialization, share = = > because the flag array will be restored after each trial
        for(int i = 0; i<= matrix.length-1; ++i){
            for(int j=0; j<=matrix[0].length-1; ++j){//Take all the grids in each row and column as the starting point and start trying
                if(dfs(matrix, word, i, j, 0, flag)==true)return true;//If one is found, complete the task + stop the attempt and return true immediately
            }
        }
        return false;//If all attempts fail, false is returned. / / if a single attempt fails, no return will be made
    }
    public boolean dfs(char[][] matrix, String word, int i, int j, int count, boolean flag[][]){
        if(0<=i && i<= matrix.length-1 && 0<=j && j<=matrix[0].length-1){//Unified interception = = > [pruning]
            if(matrix[i][j] == word.charAt(count) && flag[i][j]==false){//Match++
                ++count;//You can also use count+1 later
                if(count == word.length())return true;//If there is a complete match, it will automatically stop. / / this is the only true source of the full text
                flag[i][j] = true;//[try to change flag] (corresponding to restoring flag below)
                //The following recursive structure is similar to the recursion of a quadtree:
                if(dfs(matrix, word, i+1, j, count, flag)
                || dfs(matrix, word, i-1, j, count, flag)
                || dfs(matrix, word, i, j+1, count, flag)
                || dfs(matrix, word, i, j-1, count, flag)
                )return true;//This return true has an if and serves to pass true. It is not the source
                flag[i][j] = false;//[restore flag] / / note that "restore" (such as count) is not required for normal value transfer, but here.
            }                                   //The flag [] [] array passes a pointer (instead of providing a copy), and the recursive branches share one
        }
        return false;
    }
}//Time: O(rows*cols*3^word)//3 because you can't turn back and reduce one way
//Space: 1)flag space o (rows * cols) 2) stack space O(word)
//If there is an optimization of KMP pattern string similar to linear matching, it will be faster

2. Queen n

Niuke N Queen
Both methods are based on dynamic programming (backtracking):

Method 1: two-dimensional array + 4 directions exploration + starting point only from the first line + set two-dimensional flag to indicate whether it is possible to create a new child

Method 2: one bit array (I and A[i] represent rows and columns respectively) + one-way exploration

The essence of backtracking is "scientific exhaustion"

Time complexity O (n!)

3. Motion range of robot

There is a square with m rows and n columns on the ground. A robot starts to move from the grid with coordinates 0 and 0. Each time, it can only move one grid in the left, right, upper and lower directions, but it cannot enter the grid where the sum of digits of row coordinates and column coordinates is greater than k. For example, when k is 18, the robot can enter the grid (35,37) because 3 + 5 + 3 + 7 = 18. However, it cannot enter the grid (35,38) because 3 + 5 + 3 + 8 = 19. How many grids can the robot reach?

[general idea] dfs + pruning

public class Solution {//Adjacent grid movement / / it is difficult to jump steps due to the addition (nonlinearity) of each bit, so the adjacent exploration method is used
    int count = 0;
    public int movingCount(int threshold, int rows, int cols) {
        boolean flag[][] = new boolean[rows][cols];//Has the flag grid been here? The flag here is irreversible
        dfs(threshold, rows, cols, 0, 0, flag);//Explore from (0,0)..
        return count;
    }
    public void dfs(int threshold, int rows, int cols, int i, int j, boolean flag[][]){
        if(0<=i && i<=rows-1 && 0<=j && j<=cols-1){
            if(flag[i][j] == false && i/10 + i%10 + j/10 + j%10 <= threshold){//i. j belongs to [0,99]
                ++count;
                flag[i][j] = true;//No restore
                //If a block does not match, there is no need to try again around it
                dfs(threshold, rows, cols, i+1, j, flag);
                dfs(threshold, rows, cols, i-1, j, flag);
                dfs(threshold, rows, cols, i, j+1, flag);
                dfs(threshold, rows, cols, i, j-1, flag);
            }
        }
    }
}//Time O(rows*cols) space O(rows*cols)
//We can't use a two-tier for loop because this problem requires connected space, so we must choose one of bfs and dfs
//I feel that the modification is to find the way through the maze

4. Number of islands

A two-dimensional matrix is given, where 1 is the island and 0 is the sea. How many islands are there in total (the connection area of 1)?

public class Solution {
    static int count = 0;//Number of black areas (number of islands)
    public static void main(String[] args) {
        int[][] bitmap = {{1, 0, 0}, {0, 0, 1}, {0, 0, 1}};
        System.out.println("connectedComponts(bitmap) = " + connectedComponts(bitmap));
    }
    public static int connectedComponts (int[][] bitmap) {
        //boolean black = false;
        int rows = bitmap.length;
        int cols = bitmap[0].length;
        boolean flag[][] = new boolean[rows][cols];
        for(int i=0; i<=rows-1; ++i){
            for(int j=0; j<=cols-1; ++j){
                dfs(i,j,rows,cols,flag,bitmap, false);//Each time you go in from 1, the total black area will be + 1
            }
        }
        return count;
    }
    public static void dfs(int i, int j, int rows, int cols, boolean[][] flag, int[][] bitmap, boolean black){
        if(0<=i && i<=rows-1 && 0<=j && j<=cols-1){//In the area
            if(flag[i][j] == false && bitmap[i][j]==1){
                if(black == false){//Here is the core of the whole!!!
                    count++;
                }
                flag[i][j] = true;//walk
                dfs(i+1,j,rows,cols,flag,bitmap, true);
                dfs(i-1,j,rows,cols,flag,bitmap, true);
                dfs(i,j+1,rows,cols,flag,bitmap, true);
                dfs(i,j-1,rows,cols,flag,bitmap, true);
            }
        }
    }
}//The time complexity is O(cols*rows), because each land will be marked by flag when it is walked once at most, and it will jump out when it is encountered later.

Interval type

1. Longest palindrome substring

Given string A and its length n, please return the length of the longest palindrome substring.
Input: "abc1234321ab",12
Return value: 7

Method 1: violent solution (exhaustive)
i. j the double cycle represents the coordinates of the beginning and end, and then O(N) determines whether it is symmetrical
Time O(N^3)
Space O(1)

Method 2: dynamic programming
DP [i] [j] stores the string that starts with I and ends with j
P(i,j) <-- P(i+1,j−1)
Time O(N^2)
Space O(N^2)

Method 3: central expansion method
Starting at each point,
Expand left and right at the same time to judge whether it is equal
Time O(N) x O(N) = O(N^2)
Space O(1)

public class Solution {
    public int getLongestPalindrome(String A, int n) {
        int maxLen = 0;
        for(int i=0; i<=n-2; ++i){//i==n-1 skip
            int len1 = expand(A, i, i);//1) Single center
            int len2 = expand(A, i, i+1);//2) Double center
            int len = len1 > len2 ? len1 : len2;
            if(len > maxLen) maxLen = len;
        }
        return maxLen;
    }
    public int expand(String A, int left, int right){
        while(0<=left && right<=A.length()-1 && A.charAt(left)==A.charAt(right)){
            --left;
            ++right;
        }
        return right-left-1;//right-left+1-2
    }
}//Time O(N^2) space O(1)

Method 4: Manacher algorithm
Time O(N)
Space o
The algorithm is more complex. I'll write it down later

2. Stone game

A and B are playing games with piles of stones. Even piles of stones are arranged in a row, and each pile has a positive integer of stones.
The game is decided by who has the most stones in his hand. The total number of stones is odd, so there is no draw.
A and B take turns. A starts first. Each turn, the player takes the whole pile of stones from the beginning or end of the line. This situation continues until there are no more stone piles. At this time, the player with the most stones in his hand wins.
Assuming that both A and B play their best, it returns true when A wins the game and false when B wins the game.

Method 1: dynamic programming

Triangular ladder dp array, I and j are the left and right boundaries of continuous stone pile respectively, and dp[i][j] represents the number of leading stones
Initialize n stones in the bottom layer as the number of stone piles, and then add a pile of stones (left / right) at the boundary each time
dp[i][j] = Math.max(piles[i] - dp[i + 1][j], piles[j] - dp[i][j - 1]);
res= dp[0][len-1]
Time O(N^2)
Space O(N^2) can be optimized to O(N)

Method 2: directly return true

==>If both are the best strategy, the first will win

3. Maximum sum of continuous subarrays

Enter an integer array with both positive and negative numbers. One or more consecutive integers in the array form a sub array. Find the maximum value of the sum of all subarrays. The required time complexity is O(n)

//This is the method described in the practical algorithm class
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if(array.length ==0)return 0;
        int max = Integer.MIN_VALUE;//Global maximum / / here max=array[0] can also be used)
        int currentSum = 0;//Adjacent maximum value: fuse when it is less than 0, and the minimum value is 0
        for(int i =0; i<=array.length-1; ++i){
            currentSum += array[i];
            if(currentSum > max) max=currentSum;
            if(currentSum<0)currentSum=0;
        }
        return max;
    }
}
//Time O(N) space O(1)

4. Maximum rectangle (continuous two-dimensional array)

This kind of problem is a two-dimensional upgraded version of the maximum sum of continuous subarrays above,
The core idea is: Transform and compress to one-dimensional problem, that is, compress a vertical row to a value, traverse it with double loops, and determine the upper and lower bounds start/end respectively

public class Main {
    public int MaxMatrix(int[][]matrix) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        int res = Integer.MIN_VALUE;
        for(int begin = 0; begin<=rows-1; ++begin){//Upper boundary
            int[] line = new int [cols];//Sum of each column / / after modifying the upper boundary, clear it and start again
            for(int end = begin; end<= rows-1; ++end){//Lower boundary
                //Calculate column elements and
                for(int j=0; j<=cols-1; ++j){
                    line[j] += matrix[end][j];//The lower boundary calculates and updates the value of the lower line [] array every row down
                                            //The upper boundary is fixed, and the lower boundary is calculated successively = > avoid repeated calculation
                }
                res = Math.max(res,line[0]);
                int sum = 0;
                for(int j=0; j<=cols-1; ++j){
                    sum += line[j];
                    res = Math.max(res,sum);//Take maximum
                    if(sum<0)sum=0;//Less than zero, set to zero
                }
            }
        }
        return res;
    }
}//The idea of this problem is: from the maximum sum of one-dimensional continuous subarrays, and then extended to the traversal of the upper and lower bounds, the two-dimensional problem can be calculated.
//Time complexity O(N^3) / / should be the optimal solution

5. Consolidation interval

Methods: sort according to the left endpoint of the interval, and then traverse to the right;
Continuously update the right endpoint of the merged interval,
If the right end point after merging is less than the left end point of the next, restart an interval;
Time complexity O(N*logN): sort first and then traverse linearly

6. Archery

Methods: sort according to the right endpoint of the interval, and then traverse to the right;
If the right end point is smaller than the left end point of the next one, change to the next right end point and reopen;
Time complexity O(N*logN): sort first and then traverse linearly

public class Solution {
    // Greedy strategy: first sort the left and right intervals according to the right endpoint, and then scan from small to large,
    // If the right endpoint is smaller than the left endpoint, replace it with the right endpoint of this interval (and count + +), and then continue scanning
    public int findMinArrowShots (int[][] targets) {//Each interval represents the upper and lower bounds of the target and shoots through the target as many times as possible
        if(targets == null || targets.length==0) return 0;
        Arrays.sort(targets, new MyComparator());
        int res = 1;
        int lastEnd = targets[0][1];
        for(int i=1; i<targets.length; ++i){
            if(targets[i][0] > lastEnd){
                res++;
                lastEnd = targets[i][1];
            }
        }
        return res;
    }
	//Special comparison methods need to be written by yourself:
    //Implement the Comparator interface class and override the compare method.
    class MyComparator implements Comparator<int[]>{
        public int compare(int[] X, int[] Y){
            return X[1] - Y[1];
        }
    }
}

Backpack type

1. Partition and subset [0-1 knapsack problem]

==The problem is equivalent to: the sum of discontinuous subsets is K (k=sum/2)

The difference from [0-1 knapsack problem] is that the knapsack should be < = k, here is = = K

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        for(int num : nums)sum += num;
        if((sum & 1) == 1)return false;//Bit operation, see if the last bit of binary is 1
        int target = sum/2;
        boolean[] dp = new boolean[1+target];
        dp[0] = true;
        for(int i=0; i<=nums.length-1; ++i){//nums[i]
            boolean[] mem = new boolean[1+target];//For recording; The spatial optimization method is from back to front
            for(int k=0; k<=target; ++k){//dp[k]
                if(dp[k]==true) mem[k]=true;
            }
            for(int k=0; k<=target; ++k){
                if(mem[k]==true && k + nums[i]<=target){
                    dp[k + nums[i]]=true;
                }
            }
        }
        return dp[target];
    }
}

Space optimization:

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        for(int num : nums)sum += num;
        if((sum & 1) == 1)return false;
        int target = sum/2;
        boolean[] dp = new boolean[1+target];
        dp[0] = true;
        for(int i=0; i<=nums.length-1; ++i){
            for(int k=target; k>=0; --k){//dp[k] from back to front
                if(k-nums[i]>=0 && dp[k-nums[i]]==true)dp[k]=true;//optimization
            }
        }
        return dp[target];
    }
}//Time O(len * target)
//Space O(target)

2. Change [full backpack problem]

  • [0 / 1 knapsack problem]: select each element at most once
  • [complete knapsack problem]: each element can be selected repeatedly
  • [classified knapsack problem]: there are multiple knapsacks with different things, which need multiple traversal
class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];//I and dp[i] are the total amount and the number of coins respectively
        Arrays.fill(dp, Integer.MAX_VALUE-1);//
        dp[0] = 0;//
        for(int i=1; i<=amount; ++i){
            for(int j=0; j<=coins.length-1; ++j){
                if(i-coins[j]>=0 && dp[i-coins[j]]+1 < dp[i]){
                    dp[i] = dp[i-coins[j]]+1;
                }
            }
        }
        if(dp[amount]<=amount) return dp[amount];//Here it is judged that the content is equal to
        else return -1;//If no method is found, dp[i] is still the initial value
    }
}//Time O(amount*coin) space O(amount)

3. One and zero [classification knapsack problem]

Give you a binary string array strs and two integers m and n.
Please find out and return the size of the largest subset of strs. There are at most m zeros and n ones in this subset.
If all elements of x are also elements of Y, set x is a subset of set y.

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][] dp = new int[m+1][n+1];//The first row and the first column are automatically initialized to 0
        for(String str:strs){//
            int zeros = 0;
            int ones = 0;
            for(char c:str.toCharArray()){//Use two advanced for loops in succession
                if(c=='1')++ones;
                else ++zeros;
            }
            for(int i=m; i>=0; --i){//The contents of the backpack are in the outer layer and dp in the inner layer
                for(int j=n; j>=0; --j){//Backpack classic fallback--
                    if(i-zeros>=0 && j-ones>=0 && dp[i][j] < dp[i-zeros][j-ones] + 1){//Requirements: 1. Countless groups overflow 2. Better talent update
                        dp[i][j] = dp[i-zeros][j-ones] + 1;
                    }
                }
            }                    
        }
        return dp[m][n];
    }
}
//Time complexity O(S*M*N), where S is the number of strs [] elements
//Spatial complexity O(M*N)

In this paper, the characteristics of dynamic programming are briefly described, and then the general classification of problems is given;
Then, the solution of high-frequency algorithm problem and detailed annotation analysis are given according to the problem type;
I hope I can give you an overview of dynamic planning and help you brush questions for the test!

Tags: Dynamic Programming

Posted on Wed, 24 Nov 2021 14:13:23 -0500 by michalchojno