【LeetCode】动态规划—646. 最长数对链(附完整Python/C++代码)
动态规划—646. 最长数对链
- 前言
- 题目描述
- 基本思路
- 1. 问题定义
- 2. 理解问题和递推关系
- 3. 解决方法
- 3.1 动态规划方法
- 3.2 贪心方法
- 4. 进一步优化
- 5. 小总结
- 代码实现
- Python
- Python3代码实现
- Python 代码解释
- C++
- C++代码实现
- C++ 代码解释
- 总结
前言
在这个问题中,我们需要找到可以形成的最长数对链。数对 (a, b)
的链要求 a < b
,并且数对链的连接需要满足 b1 < a2
。这类似于寻找最长递增子序列的问题,可以通过动态规划或者贪心算法来解决。
贪心算法通过将数对按右端排序,并逐步选择满足条件的数对,能够在更短的时间内解决问题。本文将详细介绍动态规划和贪心策略,并提供 Python 和 C++ 代码示例,帮助你理解并掌握这一问题的解法。
题目描述
基本思路
1. 问题定义
给定一组数对 pairs,其中每个数对由两个整数组成 (a, b)
,并且 a < b
。一条 数对链 是指可以将数对 (a1, b1)
和 (a2, b2)
连接起来,满足 b1 < a2
。你需要找到最长的数对链。
2. 理解问题和递推关系
这个问题类似于 最长递增子序列 的问题。我们需要选择数对,并构建满足条件的数对链,使得链的长度最大化。两种解法是常见的:
动态规划:对于每一个数对,检查它之前的所有数对是否满足 b1 < a2
,如果满足,则更新当前数对能构成的最长链。
贪心策略:通过对数对的右端 b
进行排序,贪心地选择每一个数对,确保尽可能形成最长的数对链。
- 动态规划方法
- 首先,将数对按照左端
a
进行升序排序,或者按照右端 b 进行升序排序。 - 定义
dp[i]
为以第 i 个数对为结尾的最长数对链的长度。 - 对于每一个数对
pairs[i]
,遍历之前的所有数对pairs[j]
,检查pairs[j][1] < pairs[i][0]
,即数对是否可以连接。如果可以,则更新 d p [ i ] = m x ( d p [ i ] , d p [ j ] + 1 ) dp[i] = mx(dp[i], dp[j] + 1) dp[i]=mx(dp[i],dp[j]+1)。 - 最终,答案为
max(dp)
,即最长数对链的长度。
- 首先,将数对按照左端
- 贪心方法
- 首先,将数对按照右端
b
进行升序排序。 - 贪心地选择每个数对,在选择时保证其左端
a
大于上一个数对的右端b
,以确保形成最长链。 - 最终计数即为链的长度。
- 首先,将数对按照右端
3. 解决方法
3.1 动态规划方法
- 排序后,使用动态规划求解最优解。遍历每个数对,更新每个数对能够形成的最长链。
3.2 贪心方法
- 排序后,通过贪心策略选择尽可能多的数对来构成最长链。
4. 进一步优化
- 贪心方法 的时间复杂度是 O ( n l o g n ) O(n log n) O(nlogn),因为排序需要 O ( n l o g n ) O(n log n) O(nlogn) 的时间,而遍历一遍数对仅需要 O ( n ) O(n) O(n) 的时间。相比之下,动态规划的时间复杂度为 O ( n 2 ) O(n^2) O(n2),适合小规模数据。贪心方法在时间效率上更优。
5. 小总结
- 动态规划方法可以通过递推公式解决,但时间复杂度较高,适合较小规模的输入。
- 贪心方法是更优的选择,能够在 O ( n l o g n ) O(n log n) O(nlogn) 的时间复杂度内解决问题,适用于大规模输入。
以上就是最长数对链问题的基本思路。
代码实现
Python
Python3代码实现
class Solution:def findLongestChain(self, pairs: list[list[int]]) -> int:# 按照数对的第二个元素(右端点)进行升序排序pairs.sort(key=lambda x: x[1])# 初始化计数器和当前数对的结束位置cur_end = float('-inf')count = 0# 遍历每个数对for pair in pairs:# 如果当前数对可以与上一个数对连接if pair[0] > cur_end:cur_end = pair[1] # 更新结束位置count += 1 # 更新数对链长度return count
Python 代码解释
- 排序:首先按照数对的右端
b
进行升序排序,以便我们可以贪心地选择更多的数对。 - 贪心选择:遍历每个数对,检查其左端
a
是否大于当前链的结束位置cur_end
,如果满足条件,则更新链的结束位置,并增加链的长度。 - 返回结果:最终返回最长数对链的长度。
C++
C++代码实现
class Solution {
public:int findLongestChain(vector<vector<int>>& pairs) {// 按照数对的第二个元素(右端点)进行升序排序sort(pairs.begin(), pairs.end(), [](vector<int>& a, vector<int>& b) {return a[1] < b[1];});int cur_end = INT_MIN; // 当前数对链的结束位置int count = 0; // 初始化数对链的长度// 遍历每个数对for (const auto& pair : pairs) {if (pair[0] > cur_end) { // 如果当前数对可以连接cur_end = pair[1]; // 更新链的结束位置count++; // 增加链的长度}}return count; // 返回最长数对链的长度}
};
C++ 代码解释
- 排序:对数对的右端进行升序排序,方便后续的贪心选择。
- 贪心选择:通过遍历数对,判断是否可以将当前数对加入链中。如果当前数对的左端大于前一个数对的右端,就可以将其加入,并更新链的长度。
- 返回结果:最终返回最长数对链的长度。
总结
- 动态规划方法能够通过递推计算每个数对的最长链,但时间复杂度较高,为 O ( n 2 ) O(n^2) O(n2)。
- 贪心算法通过排序和逐步选择,能够在 O ( n l o g n ) O(n log n) O(nlogn) 的时间内解决问题,是更高效的解法。
- 本文提供的 Python 和 C++ 实现展示了贪心算法的高效性,希望能够帮助你解决类似的数对链问题。