随想录日记part42
t i m e : time: time: 2024.04.14
主要内容:今天开始要学习动态规划的相关知识了,今天的内容主要涉及:最长递增子序列 ;最长连续递增序列 ;最长重复子数组 ;最长公共子序列;不相交的线 ;最大子序和动态规划
- 300.最长递增子序列
- 674. 最长连续递增序列
- 718. 最长重复子数组
- 1143.最长公共子序列
- 1035.不相交的线
- 53. 最大子序和动态规划
动态规划五部曲:
【1】.确定dp数组以及下标的含义
【2】.确定递推公式
【3】.dp数组如何初始化
【4】.确定遍历顺序
【5】.举例推导dp数组
Topic1最长递增子序列
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度
2.确定递推公式:
//位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列 + 1 的最大值。
if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
3.dp数组如何初始化
Arrays.fill(dp, 1);
4.确定遍历顺序
从递归公式其实已经可以看出,一定是从前向后遍历,因为dp[i],依靠dp[i - 1]的数值。
5.举例推导dp数组
输入:[0,1,0,3,2],dp数组的变化如下:
代码如下:
class Solution {public int lengthOfLIS(int[] nums) {int len = nums.length;// dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度int[] dp = new int[len];Arrays.fill(dp, 1);if (len == 1)return 1;dp[0] = 1;int max_value = 1;for (int i = 1; i < len; i++) {for (int j = 0; j < i; j++) {if (nums[i] > nums[j])dp[i] = Math.max(dp[j] + 1, dp[i]);}max_value = Math.max(max_value, dp[i]);}return max_value;}
}
时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( n ) O(n) O(n)
Topic2最长连续递增序列
题目:
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度
2.确定递推公式:
if(nums[i]>nums[i-1])dp[i]=dp[i-1]+1;
3.dp数组如何初始化
Arrays.fill(dp, 1);
4.确定遍历顺序
从递归公式其实已经可以看出,一定是从前向后遍历,因为dp[i],依靠dp[i - 1]的数值。
5.举例推导dp数组
已输入nums = [1,3,5,4,7]为例,dp数组状态如下:
class Solution {public int findLengthOfLCIS(int[] nums) {int len = nums.length;int[] dp = new int[len];Arrays.fill(dp, 1);int result = 1;for (int i = 1; i < len; i++) {if (nums[i] > nums[i - 1]) {dp[i] = dp[i - 1] + 1;}result = Math.max(result, dp[i]);}return result;}
}
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)
Topic3最长重复子数组
题目:
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。 (特别注意: “以下标i - 1为结尾的A” 标明一定是 以A[i-1]为结尾的字符串 )
2.确定递推公式:
根据dp[i][j]的定义,dp[i][j]的状态只能由dp[i - 1][j - 1]推导出来。即当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;根据递推公式可以看出,遍历i 和 j 要从1开始!
3.dp数组如何初始化
for(int i=0;i<=nums1.length;i++) dp[i][0]=0;for(int i=0;i<=nums2.length;i++) dp[0][i]=0;
4.确定遍历顺序
外层for循环遍历A,内层for循环遍历B。
5.举例推导dp数组
拿示例1中,A: [1,2,3,2,1],B: [3,2,1,4,7]为例,画一个dp数组的状态变化,如下:
class Solution {public int findLength(int[] nums1, int[] nums2) {// dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。 (特别注意: “以下标i -1为结尾的A” 标明一定是 以A[i-1]为结尾的字符串 )int[][] dp = new int[nums1.length + 1][nums2.length + 1];for (int i = 0; i <= nums1.length; i++)dp[i][0] = 0;for (int i = 0; i <= nums2.length; i++)dp[0][i] = 0;int result = 0;for (int i = 1; i < nums1.length + 1; i++) {for (int j = 1; j < nums2.length + 1; j++) {if (nums1[i - 1] == nums2[j - 1])dp[i][j] = dp[i - 1][j - 1] + 1;result = Math.max(result, dp[i][j]);}}return result;}
}
Topic4最长公共子序列
题目:
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]
2.确定递推公式:
主要就是两大情况:1.text1[i - 1] 与 text2[j - 1]相同 2.text1[i - 1] 与 text2[j - 1]不相同
如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。
即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
3.dp数组如何初始化
for(int i=0;i<=nums1.length;i++) dp[i][0]=0;for(int i=0;i<=nums2.length;i++) dp[0][i]=0;
4.确定遍历顺序
外层for循环遍历A,内层for循环遍历B。
5.举例推导dp数组
以输入:text1 = “abcde”, text2 = “ace” 为例,dp状态如图:
class Solution {public int longestCommonSubsequence(String text1, String text2) {int len1 = text1.length();int len2 = text2.length();int[][] dp = new int[len1 + 1][len2 + 1];for (int i = 0; i <= len1; i++)dp[i][0] = 0;for (int i = 0; i <= len2; i++)dp[0][i] = 0;int result = 0;for (int i = 1; i < len1 + 1; i++) {for (int j = 1; j < len2 + 1; j++) {if (text1.charAt(i - 1) == text2.charAt(j - 1))dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);result = Math.max(result, dp[i][j]);}}return result;}
}
时间复杂度: O ( n ∗ m ) O(n*m) O(n∗m)
空间复杂度: O ( n ∗ m ) O(n*m) O(n∗m)
Topic5不相交的线
题目:
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]
2.确定递推公式:
主要就是两大情况:1.text1[i - 1] 与 text2[j - 1]相同 2.text1[i - 1] 与 text2[j - 1]不相同
如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。
即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
3.dp数组如何初始化
for(int i=0;i<=nums1.length;i++) dp[i][0]=0;for(int i=0;i<=nums2.length;i++) dp[0][i]=0;
4.确定遍历顺序
外层for循环遍历A,内层for循环遍历B。
5.举例推导dp数组
class Solution {public int maxUncrossedLines(int[] nums1, int[] nums2) {int len1 = nums1.length;int len2 = nums2.length;int[][] dp = new int[len1 + 1][len2 + 1];for (int i = 0; i <= len1; i++)dp[i][0] = 0;for (int i = 0; i <= len2; i++)dp[0][i] = 0;int result = 0;for (int i = 1; i < len1 + 1; i++) {for (int j = 1; j < len2 + 1; j++) {if (nums1[i - 1] == nums2[j - 1])dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);result = Math.max(result, dp[i][j]);}}return result;}
}
时间复杂度: O ( n ∗ m ) O(n*m) O(n∗m)
空间复杂度: O ( n ∗ m ) O(n*m) O(n∗m)
Topic6最大子序和动态规划
题目:
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]
2.确定递推公式:
dp[i]只有两个方向可以推出来:
dp[i - 1] + nums[i],即:nums[i]加入当前连续子序列和
nums[i],即:从头开始计算当前连续子序列和
一定是取最大的,所以dp[i] = max(dp[i - 1] + nums[i], nums[i]);
3.dp数组如何初始化
dp[0] = nums[0]
4.确定遍历顺序
递推公式中dp[i]依赖于dp[i - 1]的状态,需要从前向后遍历。
5.举例推导dp数组
以示例一为例,输入:nums = [-2,1,-3,4,-1,2,1,-5,4],对应的dp状态如下:
class Solution {public int maxSubArray(int[] nums) {int len = nums.length;int[] dp = new int[len];Arrays.fill(dp, Integer.MIN_VALUE);int tem;dp[0] = nums[0];if (len == 0)return 0;int result = nums[0];for (int i = 1; i < len; i++) {dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);result = Math.max(result, dp[i]);}return result;}
}
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)