題目鏈接: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;
}
};