题目
题目来源:力扣(LeetCode)
给出 n 个数对。 在每一个数对中,第一个数字总是比第二个数字小。
现在,我们定义一种跟随关系,当且仅当 b < c 时,数对(c, d) 才可以跟在 (a, b) 后面。我们用这种形式来构造一个数对链。
给定一个数对集合,找出能够形成的最长数对链的长度。你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。
示例:
输入:[[1,2], [2,3], [3,4]]
输出:2
解释:最长的数对链是 [1,2] -> [3,4]
思路分析
动态规划
子序列类型动态规划,即pairs[i]能否拼接在某个pairs[j]后,并取最长子序列
由于最后结果不限制初始位置,所以先用排序预处理下,使得原始序列竟可能递增排列
状态定义
dp[i]:以pairs[i]结尾的最长数对链长度
状态转移
遍历 j=1..i-1,如果pairs[j][1] < pairs[i][0],则dp[i]=dp[j]+1
代码实现
/**
* @param {number[][]} pairs
* @return {number}
*/
var findLongestChain = function (pairs) {
if (!pairs || pairs.length === 0) return 0
if (pairs.length === 1) return 1
//排序,想到排序是因为一条链是整体升序的
pairs.sort((a, b) => a[0] - b[0])
//存储以i结尾的最长链的长度
let dp = new Array(pairs.length).fill(1)
let ans = 0
for (let i = 1; i < pairs.length; i++) {
//找到某个j,如果i能加入j,就得到一条比j更长的链,然后找到能形成的最长的
for (let j = i - 1; j > -1; j--) {
if (pairs[j][1] < pairs[i][0]) {
dp[i] = Math.max(dp[i], dp[j] + 1)
}
}
ans = Math.max(dp[i], ans)
}
return ans
};
贪心思想
- 希望找到尽可能长的数对链,贪心的思想,我们需要当前数对链的结尾值尽可能的小。
- 比如当前有[1,2],后续可以提供选择的有[3,4],[3,8],那么显然,我们优先选取[3,4],才能让选出的数对尽可能的多。
- 首先,我们对pairs中的数组,按照第二个元素升序的顺序进行排序,如此,只需按顺序遍历,即可保证不会遗漏,且能得到我们想要的结果。
- 第一个必选,然后记录当前的尾部值,只要之后的数组起始值大于当前的尾部值,结果+1。同时更新尾部值。
var findLongestChain = function (pairs) {
//先给pairs以第二位的大小从小到大拍好序
let orderedpairs = pairs.sort((a, b) => {
return a[1] - b[1]
})
//初始化count为1(把第一个节点算进去),之后只要候选的第一位大于start节点的第二位,说明可以接到后面
let count = 1
let start = orderedpairs[0]
for (let i = 1; i < orderedpairs.length; i++) {
if (orderedpairs[i][0] > start[1]) {
//可以接的话,count+1,start节点重置为新的节点
count++
start = orderedpairs[i]
}
}
return count
};