题目链接:leetcode646
思路:
- 动态规划
这题的动态规划和LIS的思想很接近,表示前i个数对的最长链长度,但LIS由于是一维的可以用二分优化查找,而本题不行。为了消除后效性,本题还有预先做一个按右边界排序的预处理,其思想和DAG上的DP很像,DAG通过拓扑排序消除后效性,拓扑排序也是DAG的DP顺序。但本题用排序是由于还要建图,复杂度并没有降低,所以高效一点就是采用排序的方式。之所以按右边界排序的原因是每次一个数对插入一条链后,对后面的贡献就是它的右边界,每次只有右边界比当前枚举的数对的右边界来得小的才有可能在这个数对的后面加上当前数对(有点绕,不过理解了就能够解决本题了,后面的贪心思想也是基于这个道理)。如果本题有明确给出数对中的数据范围,在允许的情况下可以套一个树状数组也可以解决,不需要排序。
class Solution {
public:
static bool cmp(vector<int>&A, vector<int>&B) {
return A[1]<B[1];
}
int findLongestChain(vector<vector<int>>& pairs) {
int n=pairs.size();
vector<int> dp(n, 0); //dp[i]表示前i个数对的最长链地长度
sort(pairs.begin(), pairs.end(), cmp); //预处理消除后效性,效果和拓扑排序一样
dp[0]=1;
for(int i=1; i<n; i++)
for(int j=0; j<i; j++)
dp[i]=max(dp[i],dp[j]+(pairs[i][0]>pairs[j][1]));
return dp[n-1];
}
};
- 贪心算法
发现动态规划是逆向地去考虑以当为结尾的最大链,而每次去找之前的合法的最长链传递过来。而贪心则是先按右边界排序,每次能够合法加入链中的数对的右边界则作为整个链的右边界。这样贪心可以定性地分析一下:第一次发现合法的右边界一定是不大于后面的右边界,即使后面有一个和它相等的右边界能做的也只是替换它,但对于链的长度没任何影响。而每次选取一个最小的右边界就可以尽可能地为后面的数对腾位置,这样就能使得整个链的长度尽可能地变长。
class Solution {
public:
static bool cmp(vector<int>&A, vector<int>&B) {
return A[1]<B[1];
}
int findLongestChain(vector<vector<int>>& pairs) {
int n=pairs.size();
sort(pairs.begin(), pairs.end(), cmp);
int ans=1, preMax=pairs[0][1];
for(int i=1; i<n; i++)
if(pairs[i][0]>preMax) {
ans++;
preMax=pairs[i][1];
}
return ans;
}
};