这题我选DP,因为不需要证明,直接干就行了。
方法1: DP
首先还是需要对pairs的right进行排序。一方面是为了方便chaining,另一方面是可以去重。
然后定义 dp[i] := 以pairs[i]作为结尾,最长的序列的长度。
状态转移:dp[i] = max(dp[j] + 1) where pairs[i].left > pairs[j].right and 0 < j < i.
解释:对于pairs[i],找一个最优的pairs[j],满足pairs[i].left > pairs[j].right,这样我就可以把pairs[i]追加到以pairs[j]结尾的最长序列后面了,长度+1。
边检条件:dp[0:n] = 1,每个pair以自己作为序列的最后元素,长度为1。
时间复杂度:O(n2)
空间复杂度:O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Solution { public: int findLongestChain(vector<vector<int>>& pairs) { sort(begin(pairs), end(pairs), [](const auto& p1, const auto& p2){ return p1[1] < p2[1]; }); const int n = pairs.size(); vector<int> dp(n, 1); for (int i = 0; i < n; ++i) for (int j = 0; j < i; ++j) if (pairs[i][0] > pairs[j][1]) dp[i] = max(dp[i], dp[j] + 1); return *max_element(begin(dp), end(dp)); } }; |
方法2: 贪心
和DP一样,对数据进行排序。
每当我看到 cur.left > prev.right,那么就直接把cur接在prev后面。我们需要证明这么做是最优的,而不是跳过它选后面的元素。
Case 0: cur已经是最后一个元素了,跳过就不是最优解了。
假设cur后面有next, 因为已经排序过了,我们可以得知 next.right >= cur.right。
Case 1: next.right == cur.right。这时候选cur和选next对后面的决策来说是一样的,但由于Case 0的存在,我必须选cur。
Case 2: next.right > cur.right。不管left的情况怎么样,由于right更小,这时候选择cur一定是优于next。
综上所述,看到有元素可以连接起来就贪心的选它就对了。
时间复杂度:O(nlogn)
空间复杂度:O(1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Solution { public: int findLongestChain(vector<vector<int>>& pairs) { sort(begin(pairs), end(pairs), [](const auto& p1, const auto& p2){ return p1[1] < p2[1]; }); int right = INT_MIN; int ans = 0; for (const auto& p : pairs) if (p[0] > right) { right = p[1]; ++ans; } return ans; } }; |