「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战」
题目
我们在两条独立的水平线上按给定的顺序写下 A 和 B 中的整数。
现在,我们可以绘制一些连接两个数字 A[i] 和 B[j] 的直线,只要 A[i] == B[j],且我们绘制的直线不与任何其他连线(非水平线)相交。
以这种方法绘制线条,并返回我们可以绘制的最大连线数。
示例 1:
输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。
示例 2:
输入:nums1 = [2,5,1,2,5], nums2 = [10,5,2,1,5,2]
输出:3
示例 3:
输入:nums1 = [1,3,7,1,7,5], nums2 = [1,9,2,5,1]
输出:2
提示:
1 <= nums1.length, nums2.length <= 500
1 <= nums1[i], nums2[j] <= 2000
解题思路
此题是明显的动态规划题目,而且我们再次考虑题目分析例子,可以发现这道题就是LeetCode1143.最长公共子序列的应用版本(也就是套壳),其思路与LeetCode1143.最长公共子序列完全一致,只需要改改变量名就行。求不想交的最大连线数,其实就是求最长公共子序列
- dp[i][j]的定义
dp[i][j]表示表示 nums1 中前 i 个数字 和 nums2 中前 j 个数字最多可以组成的直线数
- 状态转移方程
- 若 nums1 中第 i 个数字和 nums2 中第 j 个数字相同,则让它们匹配可以得到最大的方案数,它们之前的最大方案数为 dp[i-1][j-1]。因此此时,dp[i][j] = dp[i-1][j-1]+1。
- 若为其它情况,那么 nums1 中第 i 个数字或者 nums2 中第 j 个数字,至少有一个数字不能对方案数做出贡献(也可能两个都不能),因此此时我们需要删除掉其中的一个。若删除nums1 中第 i 个数字,则此时方案数为 dp[i-1][j];若删除nums2 中第 j 个数字,则此时方案数为 dp[i][j-1]。显然,我们应该取这两者中的较大值。也就是 dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
- dp数组如何初始化
nums1[0, i-1]和空数组的最长公共子序列自然是0,所以dp[i][0] = 0;同理dp[0][j]也是0。其他下标都是随着递推公式逐步覆盖,初始为多少都可以,那么就统一初始为0。
- 确定遍历顺序
从递推公式,可以看出,有三个方向可以推出dp[i][j],左边,左上方,上方,所以为了在递推的过程中,这三个方向都是经过计算的数值,所以要从前向后,从上到下来遍历这个矩阵
var maxUncrossedLines = function(nums1, nums2) {
let la = nums1.length;
let lb = nums2.length;
let dp = Array.from(Array(la + 1), () => Array(lb + 1).fill(0));
for (let i = 1; i <= la; i++) {
for (let j = 1; j <= lb; 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[la][lb];
};