题目描述:
在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。
现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足:
nums1[i] == nums2[j]
且绘制的直线不与任何其他连线(非水平线)相交。`在这里插入代码片`
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
根据题目描述,求最大连线数量的值,一般的思路有2个:贪心、动态规划。观察一下示例2,如果我们采用贪心的策略,结果将会如下图
即遍历过程中只要值相同,就立刻连线,不考虑后面还有多少可以连,也不考虑如果这一个不连,后面会不会有更好的选择(这里的"更好"是说可以使得整体的连线数量更多),显然和答案不一样,所以可以考虑采用动态规划的方法。
通过人为的不断尝试发现下面结果才是使得全部连线最多的结果:
而这里的不断尝试指的就是把所有可能的结果全部列举出来,看看哪一个的连接数量最多,就返回这一个。
所谓 全部列举出来就是把所有的可能都走一遍,例如当nums1[index1] == 2
的时候,可以有3个选择,
- 不连
- 和nums2[2] = 2连线;
- 和nums2[3] = 2连线;
而这3种情况之后又有很多种情况,例如,如果我们选择第二种情况那么后面的情况如下图
所谓的暴力递归就是这么个意思,暴力列举全部的结果,选出最优的情况,
暴力递归解:
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
return dp(nums1,nums2,0,0);
}
//保留暴力递归解决问题的方法,由暴力递归函数从上向下递归得到顺序的方法为自下向上
public int dp(int[] nums1, int[] nums2,int index1,int index2){
if(index1==nums1.length || index2 == nums2.length) return 0;
else{
int max = dp(nums1,nums2,index1+1,index2);
for(int i= index2;i < nums2.length;i++){
if(nums1[index1] == nums2[i]) {
max = Math.max(max,dp(nums1,nums2,index1+1,i+1)+1);
}
}
return max;
}
}
}
改数组动态规划:改法比较难解释,但是可以总结成下面几点:
- 根据递归的边界,即BaseCase来确定数组的取值范围在这个例子中每一次递归屌用改变的都是index1和index2的值,他们的边界在第一个if语句中给出了所以可以定义一个二维数组;
- 从递归的方向确定动态数组遍历求解的方向,遍历方向为递归的反方向
dp(nums1,nums2,index1+1,i+1)
根据这行代码可以知道要求第index1必须先求出index1+1的位置即从下向上遍历求值 - 确定好了遍历顺序,将原来的递归函数原封不动的拷贝到循环遍历中,然后改变一些小地方就可以了,通过观察发现改变的地方只要是
return变成了赋值语句;
递归的地方变成了数组的引用;
整个大函数的返回也变成了数组引用;
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int L1 = nums1.length,L2 = nums2.length;
int dp[][] = new int[L1+1][L2+1];
for(int index1 = L1;index1 >= 0;index1--){
for(int index2 = L2;index2 >= 0;index2--){
if(index1==nums1.length || index2 == nums2.length) dp[index1][index2] = 0;
else{
int max = dp[index1+1][index2];
for(int i= index2;i < nums2.length;i++){
if(nums1[index1] == nums2[i]) {
max = Math.max(max,dp[index1+1][i+1]+1);
}
}
dp[index1][index2] = max;
}
}
}
return dp[0][0];
}
这里就先结合题目给大家简单的说明了一下动态规划解题的一般步骤,可能不是很严谨,有些地方讲的也不是很清晰,还请多多原谅。