最长递增子序列的变种:最长数对链
字数 1345 2025-10-26 21:06:54

最长递增子序列的变种:最长数对链

题目描述
给出 n 个数对 (a, b) 的集合,其中每个数对满足 a < b。我们希望形成一个数对链,使得链中相邻的数对 (c, d) 和 (e, f) 满足 d < e(即前一个数对的第二个数小于后一个数对的第一个数)。请找出能够形成的最长数对链的长度。

示例:
输入:[[1,2], [2,3], [3,4]]
输出:2
解释:最长的数对链是 [1,2] -> [3,4](注意不能接 [2,3] 因为 2 < 3 不满足 d < e 的条件)

解题过程

步骤1:理解问题与排序预处理

  • 问题要求选择数对形成链,且链中相邻数对必须满足“前一个的第二个数 < 后一个的第一个数”。
  • 为了便于构造最长链,我们通常先对数对进行排序。排序的标准可以是按照每个数对的第一个元素升序,如果第一个元素相同则按第二个元素升序。
  • 排序后,问题转化为在排序后的序列中选取一个子序列,使得相邻数对满足链条件,且子序列最长。这类似于最长递增子序列(LIS)问题,但比较条件不同。

步骤2:定义动态规划状态

  • 定义 dp[i] 表示以第 i 个数对结尾的最长链的长度。
  • 初始时,每个数对自身可以形成一个长度为1的链,所以 dp[i] = 1 对于所有 i。

步骤3:状态转移方程

  • 对于每个 i,我们检查所有 j < i:
    如果 pairs[j][1] < pairs[i][0](即第 j 个数对的第二个数小于第 i 个数对的第一个数),那么我们可以把第 i 个数对接在第 j 个数对后面,形成更长的链。
    此时,dp[i] = max(dp[i], dp[j] + 1)。
  • 也就是说,我们寻找所有能接在 i 前面的数对 j,并取最大的链长度。

步骤4:计算与结果

  • 我们遍历 i 从 0 到 n-1,对于每个 i,遍历 j 从 0 到 i-1,进行状态转移。
  • 最终结果不是 dp[n-1],而是整个 dp 数组中的最大值,因为最长链不一定以最后一个数对结尾。

步骤5:优化考虑

  • 上述解法时间复杂度为 O(n²),类似 LIS 的朴素解法。
  • 实际上,这个问题可以通过贪心算法优化到 O(n log n):先按数对的第二个元素排序,然后贪心地选择结束最早且能接上的数对。但这里我们聚焦于线性动态规划的解法。

举例说明
输入:[[1,2], [2,3], [3,4]]
排序后(已按第一个元素排序):[[1,2], [2,3], [3,4]]
dp 初始化:[1, 1, 1]

  • i=0:前面无数对,dp[0]=1
  • i=1:检查 j=0,pairs[0][1]=2 < pairs[1][0]=2?不满足(需要严格小于),所以 dp[1] 保持 1
  • i=2:
    • j=0:pairs[0][1]=2 < pairs[2][0]=3,满足,dp[2] = max(1, dp[0]+1=2) = 2
    • j=1:pairs[1][1]=3 < pairs[2][0]=3?不满足
      最终 dp = [1, 1, 2],最大值是 2。

这样,我们得到了最长数对链的长度为 2。

最长递增子序列的变种:最长数对链 题目描述 给出 n 个数对 (a, b) 的集合,其中每个数对满足 a < b。我们希望形成一个数对链,使得链中相邻的数对 (c, d) 和 (e, f) 满足 d < e(即前一个数对的第二个数小于后一个数对的第一个数)。请找出能够形成的最长数对链的长度。 示例: 输入:[ [ 1,2], [ 2,3], [ 3,4] ] 输出:2 解释:最长的数对链是 [ 1,2] -> [ 3,4](注意不能接 [ 2,3] 因为 2 < 3 不满足 d < e 的条件) 解题过程 步骤1:理解问题与排序预处理 问题要求选择数对形成链,且链中相邻数对必须满足“前一个的第二个数 < 后一个的第一个数”。 为了便于构造最长链,我们通常先对数对进行排序。排序的标准可以是按照每个数对的第一个元素升序,如果第一个元素相同则按第二个元素升序。 排序后,问题转化为在排序后的序列中选取一个子序列,使得相邻数对满足链条件,且子序列最长。这类似于最长递增子序列(LIS)问题,但比较条件不同。 步骤2:定义动态规划状态 定义 dp[ i ] 表示以第 i 个数对结尾的最长链的长度。 初始时,每个数对自身可以形成一个长度为1的链,所以 dp[ i ] = 1 对于所有 i。 步骤3:状态转移方程 对于每个 i,我们检查所有 j < i: 如果 pairs[ j][ 1] < pairs[ i][ 0 ](即第 j 个数对的第二个数小于第 i 个数对的第一个数),那么我们可以把第 i 个数对接在第 j 个数对后面,形成更长的链。 此时,dp[ i] = max(dp[ i], dp[ j ] + 1)。 也就是说,我们寻找所有能接在 i 前面的数对 j,并取最大的链长度。 步骤4:计算与结果 我们遍历 i 从 0 到 n-1,对于每个 i,遍历 j 从 0 到 i-1,进行状态转移。 最终结果不是 dp[ n-1 ],而是整个 dp 数组中的最大值,因为最长链不一定以最后一个数对结尾。 步骤5:优化考虑 上述解法时间复杂度为 O(n²),类似 LIS 的朴素解法。 实际上,这个问题可以通过贪心算法优化到 O(n log n):先按数对的第二个元素排序,然后贪心地选择结束最早且能接上的数对。但这里我们聚焦于线性动态规划的解法。 举例说明 输入:[ [ 1,2], [ 2,3], [ 3,4] ] 排序后(已按第一个元素排序):[ [ 1,2], [ 2,3], [ 3,4] ] dp 初始化:[ 1, 1, 1 ] i=0:前面无数对,dp[ 0 ]=1 i=1:检查 j=0,pairs[ 0][ 1]=2 < pairs[ 1][ 0]=2?不满足(需要严格小于),所以 dp[ 1 ] 保持 1 i=2: j=0:pairs[ 0][ 1]=2 < pairs[ 2][ 0]=3,满足,dp[ 2] = max(1, dp[ 0 ]+1=2) = 2 j=1:pairs[ 1][ 1]=3 < pairs[ 2][ 0 ]=3?不满足 最终 dp = [ 1, 1, 2 ],最大值是 2。 这样,我们得到了最长数对链的长度为 2。