算法训练营(day53)
算法训练营(day53)
动态规划理论基础
动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。
举个例子:有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
动态规划中 dp[j]
是由 dp[j-weight[i]]
推导出来的,然后取 max(dp[j], dp[j - weight[i]] + value[i])
。
动态规划的解题步骤
- 确定
dp
数组(dp table)以及下标的含义 - 确定递推公式
dp
数组如何初始化- 确定遍历顺序
- 举例推导
dp
数组
1143. 最长公共子序列
题目链接:https://leetcode.cn/problems/longest-common-subsequence/description/
给定两个字符串 text1
和 text2
,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0
。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
- 例如,
"ace"
是"abcde"
的子序列,但"aec"
不是"abcde"
的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
解题思路
解题过程:动态规划
按照动规五部曲来分析:
- 确定dp数组(dp table)以及下标的含义
dp[j]表示容量为j的背包,最多可以背最大重量为dp[j]
本题的 dp[i][j]
:长度为 [0, i - 1]
的字符串text1与长度为 [0, j - 1]
的字符串text2的最长公共子序列
- 确定递推公式
text1[i - 1]
与text2[j - 1]
相同,则dp[i][j] = dp[i - 1][j - 1] + 1
text1[i - 1]
与text2[j - 1]
不相同,则dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])
所以递推公式是:
1 | if(t1 == t2){ |
dp数组的初始化
因为
dp[0][0]
的值会随着遍历被覆盖,所以直接初始化为0即可。
1 | int[][] dp = new int[text1.length() + 1][text2.length() + 1]; |
- 确定遍历顺序
根据递推公式可以得出,dp[i][j]
是从前到后,从左往右遍历的一个 矩阵
1 | for(int i = 1; i < text1.length() + 1; i++){ |
- 推导dp数组
详细代码
二维动规:
1 | class Solution { |
滚动数组(一维):
1 | class Solution { |
1035. 不相交的线
题目链接:https://leetcode.cn/problems/uncrossed-lines/description/
在两条独立的水平线上按给定的顺序写下 nums1
和 nums2
中的整数。
现在,可以绘制一些连接两个数字 nums1[i]
和 nums2[j]
的直线,这些直线需要同时满足满足:
nums1[i] == nums2[j]
- 且绘制的直线不与任何其他连线(非水平线)相交。
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
解题思路
解题过程:动态规划
按照动规五部曲来分析:
- 确定dp数组(dp table)以及下标的含义
dp[j]表示容量为j的背包,最多可以背最大重量为dp[j]
求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度
本题的 dp[i][j]
:长度为 [0, i - 1]
的字符串nums1与长度为 [0, j - 1]
的字符串nums2的最长公共子序列
直接沿用 题1143 的公式即可求解
- 确定递推公式
nums1[i - 1]
与nums2[j - 1]
相同,则dp[i][j] = dp[i - 1][j - 1] + 1
nums1[i - 1]
与nums2[j - 1]
不相同,则dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])
所以递推公式是:
1 | if(nums1[i - 1] == nums2[j - 1]){ |
dp数组的初始化
因为
dp[0][0]
的值会随着遍历被覆盖,所以直接初始化为0即可。
1 | int[][] dp = new int[nums1.length + 1][nums2.length + 1]; |
- 确定遍历顺序
根据递推公式可以得出,dp[i][j]
是从前到后,从左往右遍历的一个 矩阵
1 | for(int i = 1; i < nums1.length + 1; i++){ |
- 推导dp数组
详细代码
1 | class Solution { |
53. 最大子数组和
题目链接:https://leetcode.cn/problems/maximum-subarray/description/
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
解题思路
解题过程:动态规划
按照动规五部曲来分析:
- 确定dp数组(dp table)以及下标的含义
dp[j]表示容量为j的背包,最多可以背最大重量为dp[j]
本题的 dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和。
- 确定递推公式
dp[i - 1] + nums[i]
,即:nums[i]
加入当前连续子序列和nums[i]
,即:从头开始计算当前连续子序列和
所以递推公式是:**dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])
**
- dp数组的初始化
初始化 dp[0]
即为 nums[0]
- 确定遍历顺序
递推公式中 dp[i]
依赖于 dp[i - 1]
的状态,需要从前向后遍历
同时需要把 dp[i]
的最大值 res
记录下来
1 | for(int i = 1; i < nums.length; i++){ |
- 推导dp数组
详细代码
动态规划:
1 | class Solution { |
贪心递归:
1 | class Solution { |