【Code Caprice】Dynamic Programming Questions

Mind Map Summary

Since CSDN has a picture size limit, only compressed pictures can be uploaded. . .
Please add a picture description

Classification of dynamic topics:

  • knapsack problem A
  • Robbery
  • stock question
  • subsequence problem

Introductory questions

Fibonacci numbers

Topic: 509. Fibonacci Numbers

Standard DP:

public int fib(int n) {
    
    
    if (n == 0) return 0;
    // dp[i] 第 i 个斐波那契数
    int[] dp = new int[n + 1];
    // 初始化 dp 数组
    dp[0] = 0;
    dp[1] = 1;
	// 递推
    for (int i = 2; i <= n; i++) 
	    dp[i] = dp[i - 1] + dp[i - 2];
    return dp[n];
}

Rolling Array Ideas: Optimizing with Two Variables

public int fib(int n) {
    
    
    if (n == 0) return 0;
    int a = 0, b = 1;
    for (int i = 2; i <= n; i++) {
    
    
        b = a + b;
        a = b - a;
    }
    return b;
}

climbing stairs

Topic: 70. Climbing stairs

public int climbStairs(int n) {
    
    
	// dp[i] 爬到第 i 阶时的方法数量
	int[] dp = new int[n + 1];
	dp[0] = dp[1] = 1;
	for (int i = 2; i <= n; i++) 
		dp[i] = dp[i - 1] + dp[i - 2];
	return dp[n];
}

Backpack problem:

public int climbStairs(int n) {
    
    
	// dp[i] 上第 i 层的方法数量
	int[] dp = new int[n + 1];
	dp[0] = 1;	
	for (int i = 1; i <= n; i++) // 背包
		for (int j = 1; j <= 2; j++) // 物品
			if (i >= j) dp[i] += dp[i - j];
	return dp[n];
}

simple question

Climb stairs with minimum cost

Topic: 746. Climbing Stairs Using Minimum Cost

You are given an array of integers cost, where cost[i]is ithe cost to climb up the -th step of the stairs. Once you pay this fee, you can choose to climb a flight of stairs or two. You can choose to start climbing the stairs
from the steps marked with 0or with . Please calculate and return the minimum cost to reach the top of the stairs.1

输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。
public int minCostClimbingStairs(int[] cost) {
    
    
    int n = cost.length;
    // dp[i] 爬到第 i 层的最低花费
    int[] dp = new int[n + 1];
    dp[0] = dp[1] = 0;
    for (int i = 2; i <= n; i++)
        dp[i] = Math.min(dp[i - 2] + cost[i - 2], dp[i - 1] + cost[i - 1]);
    return dp[n];
}

different paths

Title: 62. Different paths

A robot is located in the upper left corner of a m x ngrid (the starting point is labeled "Start" in the figure below).
The robot can only move one step down or to the right at a time. The robot tries to reach the bottom right corner of the grid (marked "Finish" in the image below).
How many different paths are there in total?

输入:m = 3, n = 2
输出:3
解释:
---------
| s | o |
---------
| o | o |
---------
| o | e |
---------
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下
public int uniquePaths(int m, int n) {
    
    
	// dp[i][j] 到达 a[i][j] 的不同路径数
	int[][] dp = new int[m][n];
	// 原点开始一直 往下 或 往右 走, 路径都是 1
	for (int i = 0; i < m; i++) dp[i][0] = 1;
	for (int i = 0; i < n; i++) dp[0][i] = 1;
	// 递推
	for (int i = 1; i < m; i++) 
		for (int j = 1; j < n; j++) 
			// 当前路径总数 = 从左过来的路径 + 从上过来的路径
			dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
	return dp[m - 1][n - 1];
}

different paths II

Title: 63. Different Paths II

A robot is located in the upper left corner of a m x ngrid (the starting point is labeled "Start" in the figure below).
The robot can only move one step down or to the right at a time. The robot tries to reach the bottom right corner of the grid (marked "Finish" in the image below).
Now consider that there are obstacles in the grid. So how many different paths will there be from top left to bottom right?
Obstacles and empty positions in the 1grid 0are denoted by and , respectively.

输入:obstacleGrid = 
[[0,0,0],
 [0,1,0],
 [0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
public int uniquePathsWithObstacles(int[][] g) {
    
    
    int m = g.length, n = g[0].length;
    // dp[i][j] 到达 [i][j] 的不同路径数
    int[][] dp = new int[m][n];
    dp[0][0] = 0;
	// 初始化: 从起点 往右 或 往下, 能走到的初始化为 1
    for (int i = 0; i < m; i++) {
    
    
        if (g[i][0] == 1) break;
        dp[i][0] = 1;
    }
    for (int i = 0; i < n; i++) {
    
    
        if (g[0][i] == 1) break;
        dp[0][i] = 1;
    }

    for (int i = 1; i < m; i++) {
    
    
        for (int j = 1; j < n; j++) {
    
    
            if (g[i][j] == 1) continue; // 遇到障碍, 跳过
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
        }
    }
    return dp[m - 1][n - 1];
}

integer split

Title: 343. Integer Splitting

Given a positive integer n, split it into ka sum ( ) of positive integersk >= 2 and maximize the product of these integers.
Returns the largest product you can get .

输入: n = 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
public int integerBreak(int n) {
    
    
    // dp[i] 整数 i 可以被拆分出的最大乘积
    int[] dp = new int[n + 1];
    dp[1] = dp[2] = 1;
    for (int i = 3; i <= n; i++) {
    
    
        for (int j = 1; j < i; j++) {
    
    
            // 前 j 长度进行拆分的最大乘积: dp[j] * (i - j)
            // 前 j 长度不进行拆分的最大乘积: j * (i - j)
            dp[i] = Math.max(dp[i], Math.max(dp[j] * (i - j), j * (i - j)));
        }
    }
	return dp[n];
}

Different Binary Search Trees*

Topic: 96. Different Binary Search Trees

Given an integer , how many binary search treesn are there that consist of nexactly nodes and whose node values ​​vary1 from to ? Returns the number of binary search trees that satisfy the meaning of the question.n

输入:n = 3
输出:5

DP:

public int numTrees(int n) {
    
    
    int[] dp = new int[n + 1];
    dp[0] = 1;
	// 作为根的元素 1, 2, ..., n
    for (int i = 1; i <= n; i++)
	    // 根的左右子树的元素数量
	    // 根1 [0, n-1], 根2 [1, n-2], ... 根n [n-1, 0]
        for (int j = 1; j <= i; j++) 
            dp[i] += dp[j - 1] * dp[i - j];
    return dp[n];
}

recursion:

public int numTrees(int n) {
    
    
    if (n <= 1) return 1;
    int sum = 0;
    for (int i = 1; i <= n; i++)
        sum += numTrees(i - 1) * numTrees(n - i);
    return sum;
}

Summary of the knapsack problem

Can the knapsack be filled (in fact, it is seeking the maximum value of the knapsack ): dp [ j ] = max ( dp [ j ] , dp [ j − nums [ i ] ] + nums [ i ] ) dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])dp[j]=max(dp[j],dp[jnums[i]]+nums[i])

  • separate equal sum subsets
  • The Weight of the Last Stone II

Fill the knapsack with the maximum value : dp [ j ] = max ( dp [ j ] , dp [ j − w [ i ] ] + v [ i ] ) dp[j] = max(dp[j], dp[j - w [i]] + v[i])dp[j]=max(dp[j],dp[jw[i]]+v [ i ])

  • one and zero

There are several ways to fill the knapsack : dp [ j ] = dp [ j ] + dp [ j − nums [ i ] ] dp[j] = dp[j] + dp[j - nums[i]]dp[j]=dp[j]+dp[jnums[i]]

  • target and
  • Change Change II
  • Combined Sum IV
  • Advanced stair climbing

The minimum number of items to fill the backpack : dp [ j ] = min ( dp [ j ] , dp [ j − coins [ i ] ] + 1 ) dp[j] = min(dp[j], dp[j - coins[i]] + 1)dp[j]=min(dp[j],dp[jcoins[i]]+1)

  • change exchange
  • perfect square number

The order of cycles in the complete knapsack problem:

  • If you want to find the number of combinations, the outer for loop traverses the items, and the inner for traverses the backpack
  • If you are looking for the number of permutations, the outer layer for traverses the backpack, and the inner layer for loops traverses the items
  • Generally find the maximum value, the minimum value does not require the traversal order

[1,2]and [2,1]is permutation
[1,2] is combination

01 Backpack*

have NNN items and a capacity isVVV 'sbackpack,each item can only be used once
iiThe volume of item i is vi v_ivi,This is wi w_iwi
Solve which items to put into the backpack, so that the total volume of these items does not exceed the backpack capacity, and the total value is the largest

class Solution {
    
    
    // 二维数组实现
    // v - 价值数组, w - 重量数组, c - 背包容量
    static int maxValue(int[] v, int[] w, int c) {
    
    
        // dp[i][j] 有前 i 个物品可选, 最大承重为 j 的背包的物品总价值
        int[][] dp = new int[v.length + 1][c + 1];
        for (int i = 1; i <= v.length; i++) // 物品
            for (int j = 1; j <= c; j++) // 背包
                // 容量不够放本次的物品
                if (j < w[i - 1]) dp[i][j] = dp[i - 1][j];
                // 容量够放本次物品: 比较 放 与 不放 获得的最大总价值来选择
                else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
        return dp[v.length][c];
    }

    // 一维数组实现
    static int maxValue1(int[] v, int[] w, int c) {
    
    
        int[] dp = new int[c + 1];
        for (int i = 1; i <= v.length; i++) // 物品
            for (int j = c; j >= w[i - 1]; j--) // 背包
                dp[j] = Math.max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
        return dp[c];
    }

    public static void main(String[] args) {
    
    
        int[] values = {
    
     6, 3, 5, 4, 6 }; // 价值
        int[] weights = {
    
     2, 2, 6, 5, 4 }; // 重量
        int capacity = 10; // 背包容量
        System.out.println(maxValue(values, weights, capacity));
        System.out.println(maxValue1(values, weights, capacity));
    }
}

separate equal sum subsets

Title: 416. Splitting Equal Sum Subsets

You are given a non- empty array containing only positive integers . Please judge whether this array can be divided into two subsets so that the sum of the elements of the two subsets is equal.nums

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

Topic analysis: The sum of array elements is sum, if some elements can be found in the array and are sum / 2, then the array can be separated into two subsets

Convert to 01 backpack: the array of items is nums(both value and weight nums[i]), the capacity of the backpack is sum / 2, find the maximum value that the backpack can hold

public boolean canPartition(int[] nums) {
    
    
    int sum = Arrays.stream(nums).sum();
    // 奇数, 必定无法分隔
    if ((sum & 1) == 1) return false;
    // 目标子集的元素和
    int target = sum / 2;
    // 转化成 01背包问题
    // dp[i][j]代表可装物品为0-i,背包容量为j的情况下,背包内容量的最大价值
    return maxValue(nums, nums, target) == target; 
}

2D 01 Backpack:

int maxValue(int[] v, int[] w, int c) {
    
    
	// dp[i][j] 在前 i 个物品中选择, 容量为 j 的最大物品价值
	int[][] dp = new int[v.length + 1][c + 1];

	for (int i = 1; i <= v.length; i++) {
    
    
		for (int j = 1; j <= c; j++)
			if (j < w[i - 1]) dp[i][j] = dp[i - 1][j];
			else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
	}
	return dp[v.length][c];
}

One-dimensional 01 backpack:

int maxValue(int[] v, int[] w, int c) {
    
    
	int[] dp = new int[c + 1];
	for (int i = 1; i <= v.length; i++)
		for (int j = c; j >= w[i - 1] ; j--)
			dp[j] = Math.max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
	return dp[c];
}

The Weight of the Last Stone II

Title: The Weight of the Last Stone II

There is a pile of stones stonesrepresented . where stones[i]is ithe weight of the first stone.
Each turn, choose any two stones from among them , and smash them together. Suppose the weights of the stones are xand y, respectively x <= y. Then the possible outcomes of crushing are as follows:

  • If x == y, then both stones are completely crushed;
  • If x != y, then a stone xof will be completely crushed, and ya stone of weight will have a new weight of y-x.
    In the end, at most one stone will remain . Returns the smallest . If there are no stones left, return 0.
输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。

Topic analysis:

  • x == yThe corresponding stone is completely crushed (the difference is 0), and x != ythe corresponding big stone crushes the small stone and the remaining weight of the big stone is yx (the difference is y - x)
  • It can be found that the crushing process is actually the process of weight subtraction
  • For two piles of stones, let them crush the final result is: the difference between the sum of the two piles of stones
  • If you want the stone to be finally crushed to get the minimum weight, try to find two piles of stones with similar weights
public int lastStoneWeightII(int[] stones) {
    
    
    // 核心思路:任意选 i 块石头,使它们的重量趋近于总重量的一半,因为这样和另一半抵消的差值就是最小的
    // 背包容量为和的一半, 试图寻找尽量装满一半的背包的元素的和
    // 最极端就是正好找到一半, 剩下的元素构成另一半, 得出结果为 0
    int sum = Arrays.stream(stones).sum();
    int max = maxValue(stones, stones, sum / 2);
    return Math.abs(max - (sum - max));
}

2D 01 Backpack:

int maxValue(int[] w, int[] v, int c) {
    
    
	// dp[i][j] 在前 i 个物品中选择, 容量为 j 的最大物品价值
    int[][] dp = new int[w.length + 1][c + 1];
    for (int i = 1; i <= w.length; i++) {
    
    
        for (int j = 1; j <= c; j++) {
    
    
            if (j < w[i - 1]) dp[i][j] = dp[i - 1][j];
            else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
        }
    }
    return dp[w.length][c];
}

One-dimensional 01 backpack:

int maxValue(int[] v, int[] w, int c) {
    
    
	int[] dp = new int[c + 1];
	for (int i = 1; i <= v.length; i++)
		for (int j = c; j >= w[i - 1] ; j--)
			dp[j] = Math.max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
	return dp[c];
}

target and*

Subject: Goal and

You are given an array of integers numsand an integer target.
Adding '+'or '-', and then concatenating all the integers creates an expression :

  • For example, nums = [2, 1], can be added 2before '+', added 1before '-', and then concatenated to get the expression "+2-1". Returns the number of distinct expressions
    that can be constructed by the methods above that evaluate to equal targetto .
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

backtrace:

class Solution {
    
    
    public int findTargetSumWays(int[] nums, int target) {
    
    
        return dfs(nums, target, 0);
    }

    int dfs(int[] nums, int target, int idx) {
    
    
        if (idx == nums.length) {
    
    
            if (target == 0) return 1;
            return 0;
        }
        return dfs(nums, target - nums[idx], idx + 1) + dfs(nums, target + nums[idx], idx + 1);
    }
}

Memory search:

class Solution {
    
    
    Map<String, Integer> map = new HashMap<>();

    public int findTargetSumWays(int[] nums, int target) {
    
    
        return dfs(nums, target, 0, 0);
    }

    int dfs(int[] nums, int target, int sum,  int idx) {
    
    
        if (nums.length == idx) {
    
    
            if (target == sum) return 1;
            else return 0;
        }

        String key = sum + "&" + idx;
        if (map.containsKey(key)) return map.get(key);
        int val = dfs(nums, target, sum + nums[idx], idx + 1) + dfs(nums, target, sum - nums[idx], idx + 1);
        map.put(key, val);
        return val;
    }
}

Two-dimensional DP:

// sumA - 正数和, sumB - 负数和, sum - nums 的和
// sumA + sumB = target
// sumA - sumB = sum
// sumA = (target + sum) / 2
// 同时可以发现 target + sum 必须是偶数, 否则无解
// 转化成 01背包, dp[i][j] 从前 i 个数字中选出若干个, 使得被选出的数字其和为 j 的方案数目
public int findTargetSumWays(int[] nums, int target) {
    
    
    int sum = Arrays.stream(nums).sum();
    if ((target + sum) % 2 == 1) return 0; // 奇数, 无解
    // 若 target > sum, 无法凑够; target 太小导致 target < -sum,也凑不够
    if (sum < Math.abs(target)) return 0;

    // dp[i][j] 从前 i 个数字中选出若干个, 使得被选出的数字其和为 j 的方案数目
    // dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]]
    int c = (sum + target) >> 1;
    int[][] dp = new int[nums.length + 1][c + 1];
    dp[0][0] = 1; // dp[i][0] 除了 dp[0][0] = 1, 有多个数字的情况下, 和为 0 的情况可能有多个
    for (int i = 1; i <= nums.length; i++) {
    
    
        for (int j = 0; j <= c; j++) {
    
     // j 从 0开始, 因为数字和可以为 0 (这点和背包有区别)
            // 容量有限, 无法更新
            if (j < nums[i - 1]) dp[i][j] = dp[i - 1][j];
            // 可以选择第 i 个数字 nums[i - 1], 也可以不选, s两者和
            else dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i - 1]];
        }
    }
    return dp[nums.length][c];
}

One-dimensional:

public int findTargetSumWays(int[] nums, int target) {
    
    
    int sum = Arrays.stream(nums).sum();
    if ((target + sum) % 2 == 1) return 0;
    if (sum < Math.abs(target)) return 0;

    // dp[j] 选出数字和为 j 的方案数目 dp[j] += dp[j - nums[i]]
    int c = (sum + target) >> 1;
    int[] dp = new int[c + 1];
    dp[0] = 1;
    for (int i = 1; i <= nums.length; i++) {
    
    
        for (int j = c; j >= 0; j--) {
    
     // j 从 0开始, 因为数字和可以为 0 (这点和背包有区别)
            // 容量有限, 无法更新
            if (j < nums[i - 1]) continue;
            // 可以选择第 i 个数字 nums[i - 1], 也可以不选, s两者和
            dp[j] += dp[j - nums[i -    1]];
        }
    }
    return dp[c];
}

one and zero

474. One and Zero - LeetCode

Given an array of binary strings strsand two integers mand n.
Please find and strsreturn the length of the largest subset of which has at most and and . The set is if all elements of are also elements of .m0n1
xyxy

输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。

This question seeks the length of the largest subset, which can be understood as the value of the item put in each time the condition is met is 1 (that is, the quantity + 1)

dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j-n_0][k-n_1] + 1)
- n_0 为当前字符串中 0 的个数
- n_1 为当前字符串中 1 的个数

3D DP:

public int findMaxForm(String[] strs, int m, int n) {
    
    
	int len = strs.length;
	// dp[i][j][k] 在字符数组的前 i 个字符串中, 使用 j 个 0, k 个 1, 的最大字符串的个数
	int[][][] dp = new int[len + 1][m + 1][n + 1];
	for (int i = 1; i <= len; i++) {
    
     // 物品
		for (int j = 0; j <= m; j++) {
    
     // 背包1
			for (int k = 0; k <= n; k++) {
    
     // 背包2
				int n_1 = getOneNum(strs[i - 1]); // 1 的数量
				int n_0 = strs[i - 1].length() - n_1; // 0 的数量
				// 不放物品
				if (n_0 > j || n_1 > k) dp[i][j][k] = dp[i - 1][j][k];
				// 放物品
				else dp[i][j][k] = Math.max(dp[i - 1][j][k], 
											dp[i - 1][j - n_0][k - n_1] + 1); // 此处 +1 其实是价值
			}
		}
	}
	return dp[len][m][n];
}

// 获取字符中 '1' 的个数
int getOneNum(String s) {
    
    
	int cnt = 0;
	for (char c : s.toCharArray())
		if (c == '1') cnt++;
	return cnt;
}

Two-dimensional DP: (difficult to write directly, from three-dimensional optimization)

public int findMaxForm(String[] strs, int m, int n) {
    
    
	int len = strs.length;
	// dp[i][j] 在字符数组的字符串中,使用 i 个 0, j 个 1, 的最大字符串的个数
	int[][] dp = new int[m + 1][n + 1];
	for (int i = 1; i <= len; i++) {
    
    
		int n_1 = getOneNum(strs[i - 1]); // 1 的数量
		int n_0 = strs[i - 1].length() - n_1; // 0 的数量
		for (int j = m; j >= n_0; j--)
			for (int k = n; k >= n_1; k--) 
				dp[j][k] = Math.max(dp[j][k], dp[j - n_0][k - n_1] + 1);
	}
	return dp[m][n];
}

// 获取字符中 '1' 的个数
int getOneNum(String s) {
    
    
	int cnt = 0;
	for (char c : s.toCharArray())
		if (c == '1') cnt++;
	return cnt;
}

full backpack

There are N items and a knapsack of capacity V, each item has an infinite number of items available
iiThe volume of i items isvi v_ivi,This is wi w_iwi
Solve which items to put into the backpack, so that the total volume of these items does not exceed the backpack capacity, and the total value is the largest

public class Solution {
    
    
    // 朴素版 
    // v - 价值数组, w - 重量数组, c - 背包容量
    static int maxValue(int[] v, int[] w, int c) {
    
    
        int[][] dp = new int[v.length + 1][c + 1];
        for (int i = 1; i <= v.length; i++) // 物品
            for (int j = 1; j <= c; j++) // 背包容量
                for (int k = 0; k * w[i - 1] <= j; k++) // 物品数量
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - w[i - 1] * k] + v[i - 1] * k);
        return dp[w.length][c];
    }

    // 二维数组实现
    static int maxValue1(int[] v, int[] w, int c) {
    
    
        // dp[i][j] 有前 i 个物品可选, 最大承重为 j 的背包的物品总价值
        int[][] dp = new int[v.length + 1][c + 1];
        for (int i = 1; i <= v.length; i++) // 物品
            for (int j = 1; j <= c; j++) // 背包
                // 容量不够放本次的物品
                if (j < w[i - 1]) dp[i][j] = dp[i - 1][j];
                // 容量够放本次物品
                else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - w[i - 1]] + v[i - 1]);
        return dp[v.length][c];
    }

    // 一维数组实现
    static int maxValue2(int[] v, int[] w, int c) {
    
    
        int[] dp = new int[c + 1];
        for (int i = 1; i <= v.length; i++)
            for (int j = w[i - 1]; j <= c; j++)
                dp[j] = Math.max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
        return dp[c];
    }

    public static void main(String[] args) {
    
    
        int[] values = {
    
     6, 3, 5, 4, 6 }; // 价值
        int[] weights = {
    
     2, 2, 6, 5, 4 }; // 重量
        int capacity = 10; // 背包容量
        System.out.println(maxValue(values, weights, capacity)); // 朴素版
        System.out.println(maxValue1(values, weights, capacity)); // 二维数组
        System.out.println(maxValue2(values, weights, capacity)); // 一维数组
    }
}

Change Change II

Topic: 518. Change Change II - LeetCode

You are given an array of integers coinsrepresenting coins of different denominations and an array of integers amountrepresenting the total amount.
Please calculate and return the number of coin combinations that can make up the total amount. If no combination of coins can make up the total amount, return 0.
Suppose there are an infinite number of coins of each denomination.

输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

The order of cycles in the complete knapsack problem:

  • If you want to find the number of combinations, the outer for loop traverses the items, and the inner for traverses the backpack
  • If you are looking for the number of permutations, the outer layer for traverses the backpack, and the inner layer for loops traverses the items

This question is a combination problem , and the items should be traversed first and then the backpack.

[1, 2, 2]and [2, 2, 1]are the same, so are combinations (regardless of order)

    0  1  2  3  4  5 (背包容量 j)
    1  0  0  0  0  0 没有硬币的时候)
=======================
    0  1  2  3  4  5 (背包容量 j)
1   1  1  1  1  1  1
=======================
    0  1  2  3  4  5 (背包容量 j)
1   1  1  1  1  1  1
2         2  2  3  3
有了面值为 2 的硬币后:
- 如果不用,方案数还是 dp[j]- 如果用了,看看在放入这枚硬币前,也就是背包容量为 [j-coins[i]] 的时候有几种方案
- 两种情况加起来,所以就是 dp[j] = dp[j] + dp[j-coins[i]]
========================
    0  1  2  3  4  5 (背包容量 j)
1   1  1  1  1  1  1
2         2  2  3  3
5                  4

One-dimensional array solution:

public int change(int amount, int[] coins) {
    
    
    // dp[i] 凑出总金额为 i 的硬币组合数
    // 选择硬币 coins[i], 当前硬币组合数量有 dp[j - coins[i]] 种
    // 不选择硬币 coins[i], 当前硬币组合数量为 dp[j] 种
    // 总的组合数量为: dp[j] = dp[j] + dp[j - coins[i]]
    int[] dp = new int[amount + 1];
    dp[0] = 1; // 金额为 0 时, 什么都不装, 只有这一种方式
    for (int i = 0; i < coins.length; i++) // 物品
        for (int j = coins[i]; j <= amount; j++) // 背包
            dp[j] += dp[j - coins[i]];
    return dp[amount];
}

Two-dimensional array solution:

public int change(int amount, int[] coins) {
    
    
    // dp[i][j] 从前 i 个面额的硬币中选择, 凑出总金额为 j 的硬币的组合数
    int[][] dp = new int[coins.length + 1][amount + 1];
    // 初始化 dp 数组,金额为 0 时只有一种情况,也就是什么都不装
    for (int i = 0; i <= coins.length; i++) dp[i][0] = 1;
    for (int i = 1; i <= coins.length; i++) {
    
     // 物品
        for (int j = 1; j <= amount; j++) {
    
     // 背包
            if (j < coins[i - 1]) dp[i][j] = dp[i - 1][j];
            else dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i - 1]];
        }
    }
    return dp[coins.length][amount];
}

Another common way of writing:

for (int i = 1; i <= coins.length; i++) {
    
     // 物品
	for (int j = 1; j <= amount; j++) {
    
     // 背包
		dp[i][j] = dp[i - 1][j];
		if (j >= coins[i - 1]) dp[i][j] += dp[i][j - coins[i - 1]];
	}
}

Combined Sum IV

Topic: 377. Combined sums IV

You are given an array of distinct integers nums, and a target integer target. Please find out numsfrom and return targetthe number of element combinations whose sum is .
The question data guarantees that the answer fits within the range of 32-bit integers.

输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。

This question is a permutation problem , and the backpack capacity should be traversed first, and then the items should be traversed.

It is different for [1, 2]and , so it is a permutation (related to the order)[2, 1]

One-dimensional DP:

public int combinationSum4(int[] nums, int target) {
    
    
    // dp[i] 总和为 i 的元素排列个数
	// 选这个数: dp[i - nums[j]]
	// 不选这个数: dp[i]
    int[] dp = new int[target + 1];
    dp[0] = 1;
    for (int i = 1; i <= target; i++) // 背包
        for (int j = 0; j < nums.length; j++) // 物品
            if (i >= nums[j]) // 背包放的下
                dp[i] += dp[i - nums[j]];
    return dp[target];
}

Two-dimensional DP: (a bit difficult to understand)

public int combinationSum4(int[] nums, int target) {
    
    
    // dp[i][j] 在前 i 个元素中总和为 j 的排列个数
    int[][] dp = new int[nums.length + 1][target + 1];
    // 总和为 0, 一个数也不选, 视为一种情况
    for (int i = 0; i <= nums.length; i++) dp[i][0] = 1;
	for (int j = 1; j <= target; j++) // 背包
	    for (int i = 1; i <= nums.length; i++) // 物品
            for (int k = i; k > 0; k--) 
                if (j - nums[k - 1] >= 0)
                    dp[i][j] += dp[i][j - nums[k - 1]];
    return dp[nums.length][target];
}

Climbing Stairs (Advanced Version)

Original title: 70. Climbing Stairs - LeetCode

Advanced: step by step, two steps, three steps, ... until m steps. How many different ways are there to get to the top of the building?
Level 1, level 2, ... the m level is the item, and the roof is the backpack.
Each level can be used repeatedly, for example, after skipping 1 level, you can continue to jump 1 level.
Asking how many ways to jump to the top of the building is actually asking how many ways to fill your backpack.

public int climbStairs(int n) {
    
    
	// dp[i] 爬到 i 楼的方法数
	int[] dp = new int[n + 1];
	dp[0] = 1;
	for (int i = 1; i <= n; i++) // 背包
		for (int j = i; j <= m; j++) // 物品
			if (i >= j) dp[i] += dp[i - j]; // m 换成 2 可以通过 Leetcode 的爬楼梯
	return dp[n];
}

change exchange

Topic: 322. Change Exchange - LeetCode

You are given an array of integers coinsrepresenting coins of different denominations and an integer amountrepresenting the total amount.
Calculate and return the minimum . If no combination of coins can make up the total amount, return -1.
You can consider the amount of each coin to be infinite.

输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1

This question has nothing to do with the traversal order, because it is to find the minimum value.
Generally, the minimum value is sought, and the DP array needs to be initialized to the MAX value.

public int coinChange(int[] coins, int amount) {
    
    
	// dp[i] 凑到总金额为 i 需要的最少硬币个数
	int[] dp = new int[amount + 1];
	Arrays.fill(dp, 1, dp.length, Integer.MAX_VALUE); // dp[0] = 0

	for (int i = 0; i < coins.length; i++) // 物品
		for (int j = coins[i]; j <= amount; j++) // 背包
			if (dp[j - coins[i]] != Integer.MAX_VALUE)
				dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);

	return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
}

perfect square number

Title: 279. Perfect Square Numbers - LeetCode

Given an integer n, return the least number of perfect squares that sum nto .
A perfect square is an integer whose value is equal to the square of another integer; in other words, whose value is equal to the product of an integer multiplied by itself. For example, 1, 4, 9and 16are all perfect squares, but 3and 11are not .

输入:n = 12
输出:3 
解释:12 = 4 + 4 + 4

A perfect square number is an item (can be used infinitely), and a positive integer n is a knapsack. How many items are there at least when this knapsack is filled?

Items first, then backpack:

public int numSquares(int n) {
    
    
    // dp[i] 和为 i 的完全平方数的最少数量
    int[] dp = new int[n + 1];
    Arrays.fill(dp, 1, dp.length, Integer.MAX_VALUE); // dp[0] = 1
    for (int i = 1; i * i <= n; i++) // 物品
        for (int j = i * i; j <= n; j++) // 背包
            dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
    return dp[n];
}

Backpack first, then items:

public int numSquares(int n) {
    
    
	// dp[i] 和为 i 的完全平方数的最少数量
    int[] dp = new int[n + 1];
    Arrays.fill(dp, 1, dp.length, Integer.MAX_VALUE);
    for (int i = 0; i <= n; i++) // 背包
        for (int j = 1; j * j <= i; j++) // 物品
            dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
    return dp[n];
}

word splitting

Title: 139. Word Splitting - LeetCode

Given a string sand a list of strings wordDictas dictionaries. Please judge whether you can use the words that appear in the dictionary to splice it out s.
Note : It is not required to use all the words in the dictionary, and the words in the dictionary can be used repeatedly.

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
public boolean wordBreak(String s, List<String> wordDict) {
    
    
    // dp[i] s[0..i] 能否拆分成 wordDict
    boolean[] dp = new boolean[s.length() + 1];
    dp[0] = true;
    for (int i = 1; i <= s.length(); i++) // 背包
        for (int j = 0; j < i; j++) // 物品
            if (wordDict.contains(s.substring(j, i)) && dp[j])
                dp[i] = true;
    return dp[s.length()];
}

Multiple Backpacks

have NNN items and a capacity isVVV 's backpack.
SectionIIi items have at mostsi s_isipieces, the volume of each piece is vi v_ivi,This is wi w_iwi.
Solve which items to put into the backpack, so that the sum of the volume of the items does not exceed the capacity of the backpack, and the sum of the values ​​is the largest.

There is no corresponding topic on LeetCode for multiple backpacks, let's find out.

class Solution {
    
    
    static int maxValue(int[] nums, int[] w, int[] v, int c) {
    
    
        int[] dp = new int[c + 1];
        for (int i = 0; i < w.length; i++) // 物品
            for (int j = c; j >= w[i]; j--) {
    
     // 背包容量
                for (int k = 1; k <= nums[i] && k * w[i] <= j; k++) // 遍历个数
                    dp[j] = Math.max(dp[j], dp[j - k * w[i]] + k * v[i]);
                System.out.println(Arrays.toString(dp));
            }
        return dp[c];
    }

    public static void main(String[] args) {
    
    
        int[] nums = new int[] {
    
     2, 3, 2 };
        int[] w = new int[] {
    
     1, 3, 4 }; // 重量
        int[] v = new int[] {
    
     15, 20, 30 }; // 价值
        int c = 10; // 背包容量

        System.out.println(maxValue(nums, w, v, c));
    }
}

Robbery Series

Robbery

Title: 198. Robbery - LeetCode

You are a professional thief planning to steal houses along the street. There is a certain amount of cash hidden in each room. The only restrictive factor that affects your theft is that the adjacent houses are equipped with interconnected anti-theft systems. If two adjacent houses are broken into by thieves on the same night, the system will automatically call the police. .
Given an array of non-negative integers representing the amount stored in each house, calculate the maximum amount you can steal in one night without triggering the alarm.

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。
public int rob(int[] nums) {
    
    
    int n = nums.length;
    // dp[i] 偷第 i 号房屋时的最高金额
    int[] dp = new int[n + 1];
    dp[1] = nums[0];
    for (int i = 2; i <= n; i++)
        // 今天偷 或 不偷 两种选择
        dp[i] = Math.max(dp[i - 2] + nums[i - 1], dp[i - 1]);
    return dp[n];
}

Dakkakosha II

Title: 213. House Robbery II - LeetCode

You are a professional thief planning to rob houses along the street, each of which has a certain amount of cash hidden in it. All the houses in this place are in a circle , which means that the first house and the last house are right next to each other. At the same time, adjacent houses are equipped with interconnected anti-theft systems. If two adjacent houses are broken into by thieves at the same night, the system will automatically call the police .
Given an array of non-negative integers representing the amount stored in each house, calculate the maximum amount you can steal tonight without setting off the alarm.

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

Since the head and the tail are the same, remove the head and the tail respectively [0, n - 1], [1, n]steal in and , and return the maximum value of the two results

class Solution {
    
    
    public int rob(int[] nums) {
    
    
        if (nums.length == 1) return nums[0];
        return Math.max(
                help(Arrays.copyOfRange(nums, 0, nums.length - 1)),
                help(Arrays.copyOfRange(nums, 1, nums.length)));
    }

    public int help(int[] nums) {
    
    
        int n = nums.length;
        int[] dp = new int[n + 1];
        dp[1] = nums[0];
        for (int i = 2; i <= n; i++)
            dp[i] = Math.max(dp[i - 2] + nums[i - 1], dp[i - 1]);
        return dp[n];
    }
}

Robbery III TODO

Title: 337. House Robbery III - LeetCode

stock series

Stock question:

  • 121. The best time to buy and sell stocks ): buy and sell only once
  • 122. The best time to buy and sell stocks II: You can buy and sell multiple times
  • 123. The best time to buy and sell stocks III: buy and sell at most twice
  • 188. The best time to buy and sell stocks IV: buy and sell at most k times
    1. The best time to buy and sell stocks includes a freezing period: buy and sell multiple times, sell for one day with a freezing period
  • 714. The best time to buy and sell stocks includes handling fees: buy and sell multiple times, each time there is a handling fee

Best Time to Buy and Sell Stocks - Buy and Sell Once

Topic: 121. The Best Time to Buy and Sell Stocks - LeetCode

Given an array prices, its ith element prices[i]represents the price of a given stock on the th iday .
You can only choose to buy the stock on one day and sell it on a different day in the future. Design an algorithm to calculate the maximum profit you can make.
Returns the maximum profit you can make from this trade. If you can't make any profit, return 0.

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

DP:

public int maxProfit(int[] prices) {
    
    
    // dp[i] 第 i 天卖出股票(当天就卖)能获取的最大利润
    int[] dp = new int[prices.length];
    // min 记录 i 之前天数的最大值
    int min = prices[0], res = 0;
    for (int i = 0; i < prices.length; i++) {
    
    
        if (prices[i] < min) min = prices[i];
        dp[i] = prices[i] - min; // 今天的利润
        res = Math.max(res, dp[i]);
    }
    return res; 
}

Greedy: Find from left to right, the difference between the minimum value on the left and the maximum value on the right

There is no need to calculate the minimum value on the left and the maximum value on the right and make the difference. You
only need to traverse from left to right, maintain the minimum value on the left, and then maintain the difference between the current traversed value and the minimum value.

public int maxProfit(int[] prices) {
    
    
	int min = Integer.MAX_VALUE, res = 0;
	for (int i = 0; i < prices.length; i++) {
    
    
		min = Math.min(min, prices[i]);
		res = Math.max(res, prices[i] - min);
	}
	return res;
}

Best Time to Buy and Sell Stocks II - Buy and Sell Multiple Times

Topic: 122. Best Time to Buy and Sell Stocks II - LeetCode

You are given an array of integers prices, where prices[i]represents ithe price of a certain stock on day .
On each day, you can decide whether to buy and/or sell stocks. You can only hold at most one share of stock at any one time . You can also buy first and sell later on the same day. Returns the maximum .

输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
     总利润为 4 + 3 = 7 。

DP:

public int maxProfit(int[] prices) {
    
    
    int n = prices.length;
    // buy[i] 在第 i 天买入股票(不一定当天买)的最大利润
    int[] buy = new int[n];
    buy[0] = -prices[0];
    // sell[i] 在第 i 天卖出股票(不一定当卖卖)的最大利润
    int[] sell = new int[n];

    for (int i = 1; i < n; i++) {
    
    
        // 选择: [当前不买(之前买)] 或 [当前买]
        buy[i] = Math.max(buy[i - 1], sell[i - 1] - prices[i]);
        // 选择: [当前不卖(之前卖)] 或 [当前卖]
        sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]);
    }

    return sell[n - 1];
}

greedy:

public int maxProfit(int[] prices) {
    
    
    int res = 0;
    for (int i = 1; i < prices.length; i++)
        if (prices[i] > prices[i - 1])
            res += prices[i] - prices[i - 1];
    return res;
}

Best Time to Buy or Sell Stocks III - Buy or Sell Max Twice

Topic: 123. Best Time to Buy and Sell Stocks III - LeetCode

Given an array whose i i- element is ithe 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 stock before buying again).

输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
     随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
public int maxProfit(int[] prices) {
    
    
	int n = prices.length;
	// sell[i][j] 在第 i 天不持股(当天或之前卖出)的最大利润, 当前是第 j 笔交易
	int[][] sell = new int[n][3];
	// buy[i][j] 在第 i 天持股(当天或之前买入)的最大利润, 当前是第 j 笔交易
	int[][] buy = new int[n][3];
	for (int i = 0; i < 3; i++) buy[0][i] = -prices[0];

	for (int i = 1; i < n; i++) {
    
    
		for (int j = 1; j < 3; j++) {
    
    
			// 卖的必须是当前轮买的
			sell[i][j] = Math.max(sell[i - 1][j], buy[i - 1][j] + prices[i]);
			// 买之前必须把上一轮卖掉
			buy[i][j] = Math.max(buy[i - 1][j], sell[i- 1][j - 1] - prices[i]);
		}
	}

	int max = 0;
	for (int i = 0; i < 3; i++) max = Math.max(max, sell[n - 1][i]);
	return max;
}

The best time to buy and sell stocks IV - buy and sell at most k times

Topic: 188. Best Time to Buy and Sell Stocks IV - LeetCode

Given an array of integers prices, its ith element prices[i]is the price of a given stock on iday .
Design an algorithm to calculate the maximum profit you can make. You can complete at most k transactions.
Note : You cannot participate in multiple transactions at the same time (you must sell the previous stock before buying again).

输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
public int maxProfit(int k, int[] prices) {
    
    
	int n = prices.length;
	// sell[i][j] 在第 i 天不持股(当天或之前卖出)的最大利润, 当前是第 j 笔交易
	int[][] sell = new int[n][k + 1];
	// buy[i][j] 在第 i 天持股(当天或之前买入)的最大利润, 当前是第 j 笔交易
	int[][] buy = new int[n][k + 1];
	for (int i = 0; i < k + 1; i++) buy[0][i] = -prices[0];
	
	for (int i = 1; i < n; i++) {
    
    
		for (int j = 1; j < k + 1; j++) {
    
    
			// 卖的必须是当前轮买的
			sell[i][j] = Math.max(sell[i - 1][j], buy[i - 1][j] + prices[i]);
			// 买之前必须把上一轮卖掉
			buy[i][j] = Math.max(buy[i - 1][j], sell[i - 1][j - 1] - prices[i]);
		}
	}
	
	int max = 0;
	for (int i = 0; i < k + 1; i++) max = Math.max(max, sell[n - 1][i]);
	return max;
}

The best time to buy and sell stocks includes a freezing period - buy and sell multiple times, sell for one day with a freezing period

Topic: 309. The best time to buy and sell stocks includes a freezing period - LeetCode

Given an array of integers priceswhere the th prices[i]represents ithe stock price on the th day.
Design an algorithm to calculate the maximum profit. You can complete as many transactions as possible (buy and sell a stock multiple times) subject to the following constraints:

  • After selling the stock, you cannot buy the stock the next day (that is, the freezing period is 1 day).
    Note : You cannot participate in multiple transactions at the same time (you must sell the previous stock before buying again).
输入: prices = [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
public int maxProfit(int[] prices) {
    
    
    int n = prices.length;
    // sell[i] 第 i 天卖出的最大收益
    int[] sell = new int[n];
    // buy[i] 第 i 天买入的最大收益
    int[] buy = new int[n];
    // cool[i] 第 i 天冷冻的最大收益
    int[] cool = new int[n];
    buy[0] = -prices[0];
    for (int i = 1; i < n; i++) {
    
    
        // 不买 或 买(只能在冷冻期过后买)
        buy[i] = Math.max(buy[i - 1], cool[i - 1] - prices[i]);
        // 不卖 或 卖
        sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]);
        // 冷冻期只会由昨天卖出得到
        cool[i] = sell[i - 1];
    }
    return Math.max(sell[n - 1], cool[n - 1]);
}

The best time to buy and sell stocks includes handling fees - buy and sell multiple times, each time there is a handling fee

Topic: 714. The best time to buy and sell stocks includes handling fees - LeetCode

Given an array of integers prices, where prices[i]represents ithe stock price on the day; the integer feerepresents the handling fee for trading stocks.
You can complete transactions an unlimited number of times, but you need to pay a transaction fee for each transaction. If you have already bought a stock, you cannot continue to buy another stock until you sell it.
Returns the maximum value of profit.
Note : A transaction here refers to the entire process of buying, holding and selling stocks, and you only need to pay a handling fee for each transaction.

输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
解释:能够达到的最大利润:  
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
public int maxProfit(int[] prices, int fee) {
    
    
	int n = prices.length;
	// buy[i] 第 i 天时买入股票的最大收益
	int[] buy = new int[n];
	// sell[i] 第 i 天时卖出股票的最大收益
	int[] sell = new int[n];
	buy[0] = -prices[0];
	for (int i = 1; i < n; i++) {
    
    
		// 当前不买(之前买) 或 当前买
		buy[i] = Math.max(buy[i - 1], sell[i - 1] - prices[i]);
		// 当前不卖(之前卖) 或 当前卖
		sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i] - fee);
	}
	return sell[n - 1];
}

subsequence, subarray

The subsequence is discontinuous, just keep the relative order of the elements

Subarrays and substrings are continuous, and there cannot be more or less elements between elements

longest increasing subsequence

Topic: 300. Longest Increasing Subsequence - LeetCode

Given an array of integers nums, find the length of the longest strictly increasing subsequence in it .

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
public int lengthOfLIS(int[] nums) {
    
    
    // dp[i] 以 i 位置结尾的最长递增子序列的长度
    int[] dp = new int[nums.length];
    Arrays.fill(dp, 1); // 每个位置的上升子序列至少是 1
    int res = -1;
    for (int i = 0; i < nums.length; i++) {
    
    
        for (int j = 0; j < i; j++) 
            if (nums[j] < nums[i])
                dp[i] = Math.max(dp[i], dp[j] + 1);
        res = Math.max(res, dp[i]);
    }
    return res;
}

longest continuous increasing sequence

Title: 674. Longest Continuous Increasing Sequence - LeetCode

Given an unsorted array of integers, find the longest continuously increasing subsequence and return the length of that sequence.

输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。 

DP:

public int findLengthOfLCIS(int[] nums) {
    
    
	// dp[i] 以 i 位置结尾的最长连续递增序列的长度
    int[] dp = new int[nums.length];
    Arrays.fill(dp, 1); // 长度至少为 1
    int res = 1;
    for (int i = 0; i < nums.length - 1; i++) {
    
    
        if (nums[i] < nums[i + 1]) 
	        dp[i + 1] = dp[i] + 1;
        res = Math.max(res, dp[i + 1]);
    }
    return res;

greedy:

public int findLengthOfLCIS(int[] nums) {
    
    
	int cnt = 1, res = 1;
	for (int i = 0; i < nums.length - 1; i++) {
    
    
		if (nums[i] < nums[i + 1]) cnt++;
		else cnt = 1;
		res = Math.max(res, cnt);
	}
	return res;
}

longest repeating subarray

Title: 718. Longest Repeating Subarray - LeetCode

Given two integer arrays nums1and nums2, return the length of the longest subarray common to both arrays .

输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。
public int findLength(int[] nums1, int[] nums2) {
    
    
	int n = nums1.length, m = nums2.length;
	// dp[i][j] - nums1[i] 和 nums2[j] 结尾的最长重复子数组的长度
	int[][] dp = new int[n + 1][m + 1];
	int res = 0;
	for (int i = 1; i <= n; i++) {
    
    
		for (int j = 1; j <= m; j++)
			if (nums1[i - 1] == nums2[j - 1]) {
    
    
				dp[i][j] = dp[i - 1][j - 1] + 1;
				res = Math.max(res, dp[i][j]);
			}
	}
	return res;
}

longest common subsequence

Title: 1143. Longest Common Subsequence - LeetCode

Given two strings text1and text2, return the length of the longest common . Returns if no common0 .

public int longestCommonSubsequence(String s1, String s2) {
    
    
	int n = s1.length(), m = s2.length();
	char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray();
	// dp[i][j] s1 中 [0..i] 和 s2 中 [0..j] 的公共子序列长度
	int[][] dp = new int[n + 1][m + 1];
	for (int i = 1; i <= n; i++) {
    
    
		for (int j = 1; j <= m; j++) {
    
    
			if (cs1[i - 1] == cs2[j - 1])
				dp[i][j] = dp[i - 1][j - 1] + 1;
			else
				dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
		}
	}
	return dp[n][m];
}

disjoint lines

Title: 1035. Disjoint Lines - LeetCode

Write the integers in nums1and nums2.
Now, it is possible to draw nums1[i]some nums2[j]lines connecting two numbers and , these lines need to satisfy both:

  • nums1[i] == nums2[j]
  • And the line drawn does not intersect any other line (non-horizontal line).
    Note that lines cannot intersect even at endpoints: each number can only belong to one line.
    Draws lines in this way and returns the maximum number of lines that can be drawn.
输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。 
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。

Analyzing the meaning of the question, I found that this question is to find the longest common subsequence of two arrays :

var maxUncrossedLines = function (nums1, nums2) {
    
    
    const [n, m] = [nums1.length, nums2.length];
    let dp = new Array(n + 1).fill(0).map((x) => new Array(m + 1).fill(0));
    for (let i = 1; i <= n; i++)
        for (let j = 1; j <= m; j++)
			if (nums1[i - 1] === nums2[j - 1])
				dp[i][j] = dp[i - 1][j - 1] + 1;
			else
				dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
    return dp[n][m];
};

largest subarray sum

Title: 53. Maximum Subarray Sum - LeetCode

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

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
public int maxSubArray(int[] nums) {
    
    
    // dp[i] 以 i 位置结尾的元素的连续子数组的最大和
    int[] dp = new int[nums.length];
    int res = dp[0] = nums[0];
    for (int i = 1; i < nums.length; i++) {
    
    
        dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])
        res = Math.max(res, dp[i]);
    }
    return res;
}

edit distance

judgment subsequence

Topic: 392. Judging subsequences - LeetCode

Given strings s and t , determine whether s is a subsequence of t .

输入:s = "abc", t = "ahbgdc"
输出:true

Double pointer:

public boolean isSubsequence(String s, String t) {
    
    
    if (s.length() == 0) return true;
    char [] cs = s.toCharArray(), ct = t.toCharArray();
    int j = 0;
    for (int i = 0; i < ct.length; i++) {
    
    
        if (cs[j] == ct[i]) j++;
        if (j == cs.length) return true;
    }
    return false;
}

DP:

public boolean isSubsequence(String s, String t) {
    
    
    int l1 = s.length(), l2 = t.length();
    // dp[i][j] s 中 [0..i] 的串和 t 中 [0..j] 的串的公共子序列的长度
    int[][] dp = new int[l1 + 1][l2 + 1];
    for (int i = 1; i <= l1; i++)
        for (int j = 1; j <= l2; j++)
            if (s.charAt(i - 1) == t.charAt(j - 1))
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = dp[i][j - 1];

    return dp[l1][l2] == s.length();
}

different subsequences

Title: 115. Different Subsequences - LeetCode

Given a string sand a string t, scount tthe number of occurrences of in a subsequence of .

dp[i][j]: s[0..i] 和 t[0..j] 的子序列的个数

s[i] == t[j] 的情况:
- 对于 s = "rara", t = "ra", i = 3, j = 1
1. s 用最后一位 'a', 此时相当于求 "rar" 和 "r" 的子序列个数, 即 dp[i - 1][j - 1]
2. s 不用最后一位 'a', 此时相当于求 "rar" 和 "ra" 的子序列个数, 即 dp[i - 1][j]

s[i] != t[j] 的情况:
- 对于 s = "rarb", t = "ra", i = 3, j = 1
1. s 只能不用最后一位 'b', 此时相当于求 "rar" 和 "ra" 的子序列个数, 即 dp[i - 1][j]
public int numDistinct(String s, String t) {
    
    
	int n = s.length(), m = t.length();
	char[] cs = s.toCharArray(), ct = t.toCharArray();

	// dp[i][j] s[0..i] 和 t[0..j] 的子序列的个数
	int[][] dp = new int[n + 1][m + 1];
	for (int i = 0; i <= n; i++) dp[i][0] = 1;

	for (int i = 1; i <= n; i++) {
    
    
		for (int j = 1; j <= m; j++) {
    
    
			if (j > i) continue;     
			if (cs[i - 1] == ct[j - 1])
				dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
			else 
				dp[i][j] = dp[i - 1][j];
		}
	}
	return dp[n][m];
}

Delete operation for two strings

Title: 583. Deletion operation of two strings - LeetCode

Given two words word1and word2, word1return the minimum number of steps required to make and word2 equal . Each step can delete one character in any string.

输入: word1 = "sea", word2 = "eat"
输出: 2
解释: 第一步将 "sea" 变为 "ea" ,第二步将 "eat "变为 "ea"

DP 1: (edit distance idea)

public int minDistance(String s1, String s2) {
    
    
    char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray();
    int n = s1.length(), m = s2.length();
    // dp[i][j] 删除字符使得 s1 [0..i] 和 s2 [0..j] 相同的最小步数
    int[][] dp = new int[n + 1][m + 1];
    for (int i = 0; i <= n; i++) dp[i][0] = i;
    for (int i = 0; i <= m; i++) dp[0][i] = i;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (cs1[i - 1] == cs2[j - 1])
                dp[i][j] = dp[i - 1][j - 1];
            else
                dp[i][j] = Math.min(dp[i - 1][j - 1] + 2,
                        Math.min(dp[i - 1][j], dp[i][j - 1]) + 1);
    return dp[n][m];
}

DP2: Find the length of the longest common subsequence of two strings, and all other characters need to be deleted

public int minDistance(String s1, String s2) {
    
    
	int n = s1.length(), m = s2.length();
	char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray();
	// dp[i][j] s1 [0..i] 和 s2 [0..j] 的最长公共子序列的长度
	int[][] dp = new int[n + 1][m + 1];
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= m; j++)
			if (cs1[i - 1] == cs2[j - 1]) 
				dp[i][j] = dp[i - 1][j - 1] + 1;
			else
				dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
	return m + n - dp[n][m] * 2;
}

edit distance

Title: 72. Edit Distance - LeetCode

Given your two words word1and word2, return the minimum number of operations used toword1word2 convert from to .
You can perform the following three operations on a word:

  • insert a character
  • delete a character
  • replace a character
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
public int minDistance(String s1, String s2) {
    
    
	int n = s1.length(), m = s2.length();
	char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray();
	// dp[i][j] 将 s1[0..n] 变成 s2[0..m] 需要的最少操作数
	int[][] dp = new int[n + 1][m + 1];
	for (int i = 0; i <= n; i++) dp[i][0] = i;
	for (int i = 0; i <= m; i++) dp[0][i] = i;

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			// 不需要进行操作
			if (cs1[i - 1] == cs2[j - 1])
				dp[i][j] = dp[i - 1][j - 1];
			// 需要进行操作: 取 添加, 删除, 修改 的最小值
			else 
				dp[i][j] = Math.min(
					Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]
				) + 1;
	return dp[n][m];
}

palindrome skewer

palindrome skewer

Title: 647. Palindromic Substrings - LeetCode

Given a string s, please count and return the number of palindromic substrings in this string.

输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

Two-dimensional DP:

public int countSubstrings(String s) {
    
    
    char[] cs = s.toCharArray();
    // dp[i][j] [i..j] 的子串是否是回文串
    boolean[][] dp = new boolean[s.length()][s.length()];
    int res = 0;
    for (int i = cs.length - 1; i >= 0; i--)
        for (int j = i; j < cs.length; j++)
			// 由状态转移方程可知, 每一个位置的更新取决于左下角位置的状态值
			// 所以外层循环从下往上, 内层从左往右
            if (cs[i] == cs[j] && (j - i < 2 || dp[i + 1][j - 1])) {
    
    
                dp[i][j] = true;
                res++;
            }
    return res;
}

Center spread:

class Solution {
    
    
    int res = 0;

    public int countSubstrings(String s) {
    
    
        char[] cs = s.toCharArray();
        for (int i = 0; i < cs.length; i++) {
    
    
            count(cs, i, i); // 回文串长度为奇数
            count(cs, i, i + 1); // 回文串长度为偶数
        }
        return res;
    }

    void count(char[] cs, int l, int r) {
    
    
        while (l >= 0 && r < cs.length && cs[l] == cs[r]) {
    
    
            res++;
            l--;
            r++;
        }
    }
}

One-dimensional DP: (violent double pointer)

class Solution {
    
    
    public int countSubstrings(String s) {
    
    
        char[] cs = s.toCharArray();
        // dp[i] 以 s[i] 结尾的回文子串的数目
        int[] dp = new int[cs.length];
        for (int i = 0; i < cs.length; i++)
            for (int j = 0; j <= i; j++)
                if (isPalindrome(cs, j, i))
                    dp[i]++;
        return Arrays.stream(dp).sum();
    }

	// 是否是回文串
    boolean isPalindrome(char[] cs, int l, int r) {
    
    
        while (l < r)
            if (cs[l++] != cs[r--])
                return false;
        return true;
    }
}

longest palindromic substring

Topic: 5. Longest Palindromic Substring - LeetCode

Given a string s, find the longest palindromic substring sin .

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

Center spread:

class Solution {
    
    
    String res = "";

    public String longestPalindrome(String s) {
    
    
        for (int i = 0; i < s.length(); i++) {
    
    
            helper(s, i, i); // 处理奇数长度的回文串
            helper(s, i, i + 1); // 处理偶数长度的回文串
        }
        return res;
    }

    void helper(String s, int l, int r) {
    
    
        while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
    
    
            String t = s.substring(l, r + 1);
            if (t.length() > res.length()) res = t;
            l--;
            r++;
        }
    }
}

Dynamic programming:

public String longestPalindrome(String s) {
    
    
	// dp[i][j] s[i..j] 的子串是否是回文子串
	boolean[][] dp = new boolean[s.length()][s.length()];
	int l = 0, r = 0; // 记录结果的截取位置
	for (int i = s.length() - 1; i >= 0; i--)
		for (int j = i; j < s.length(); j++)
			// 由状态转移方程可知, 每一个位置的更新取决于左下角位置的状态值
			// 所以外层循环从下往上, 内层从左往右
			if (s.charAt(i) == s.charAt(j) && (j - i < 2 || dp[i + 1][j - 1])) {
    
    
					dp[i][j] = true;
					if (r - l < j - i) {
    
    
						l = i;
						r = j;
					}
			}
	return s.substring(l, r + 1);
}

longest palindromic subsequence

Title: 516. Longest Palindromic Subsequence - LeetCode

Given a string s, find the longest palindromic subsequence in it and return the length of that sequence.

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。
public int longestPalindromeSubseq(String s) {
    
    
	int n = s.length();
	char[] cs = s.toCharArray();
	// dp[i][j] s[i..j] 中的最长回文子序列的长度
	int[][] dp = new int[n][n];
	for (int i = 0; i < n; i++) dp[i][i] = 1;
	for (int i = n - 1; i >= 0; i--) {
    
    
		for (int j = i + 1; j < n; j++) {
    
    
			if (cs[i] == cs[j])
				dp[i][j] = dp[i + 1][j - 1] + 2;
			else
				dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
		}
	}
	return dp[0][n - 1];
}

Guess you like

Origin blog.csdn.net/weixin_43734095/article/details/126923969