给你一个二维整数数组 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 个信封的宽度和高度。
当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算 最多能有多少个 信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
注意:不允许旋转信封。
示例 1:
输入:envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出:3
解释:最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。示例 2:
输入:envelopes = [[1,1],[1,1],[1,1]]
输出:1提示:
1 <= envelopes.length <= 5000
envelopes[i].length == 2
1 <= wi, hi <= 104
同时控制 w和 h 两个维度并容易,因此我们考虑固定一个维度,再在另一个维度上进行选择,进而转为在另一个维度上求解最大递增子序列。例如,我们固定 w维度,那么我们将数组 envelopes中的所有信封按照 w升序排序。这样一来,我们只要按照信封在数组中的出现顺序依次进行选取,就一定保证满足:
w0≤w1≤⋯≤wk−1
然而小于等于 ≤ 和小于 <还是有区别的
当 w 值相同时,如果我们不规定 h 值的排序顺序,那么可能会有如下的情况:
排完序的结果为 [(w,h)]=[(1,1),(1,2),(1,3),(1,4)],由于这些信封的w值都相同,不存在一个信封可以装下另一个信封,那么我们只能在其中选择 1个信封。然而如果我们完全忽略 w 维度,剩下的 h维度为 [1,2,3,4],这是一个严格递增的序列,那么我们就可以选择所有的4个信封了,这就产生了错误。
因此,我们必须要保证对于每一种 w值,我们最多只能选择 1个信封。
我们可以将 h值作为排序的第二关键字进行降序排序,这样一来,对于每一种 w值,其对应的信封在排序后的数组中是按照 hhh 值递减的顺序出现的,那么这些 h值不可能组成长度超过 1的严格递增的序列,这就从根本上杜绝了错误的出现。
因此我们就可以得到解决本题需要的方法:
首先我们将所有的信封按照 w值第一关键字升序、h值第二关键字降序进行排序;
随后我们就可以忽略 w维度,求出 h维度的最长严格递增子序列,其长度即为答案。
class Solution { public: int maxEnvelopes(vector<vector<int>>& envelopes) { int n = envelopes.size(); sort(envelopes.begin(), envelopes.end(),[](const auto& e1, const auto& e2) { return e1[0]<e2[0] || (e1[0] == e2[0] && e1[1] > e2[1]); }); int len = 1; vector<int> dp(n+1, 0); dp[1] = envelopes[0][1]; for(int i =1; i < n; ++i) { if(envelopes[i][1] > dp[len]) { dp[++len] = envelopes[i][1]; } else { int left = 1; int right = len; while(left <= right) { int mid = left + (right-left)/2; if(dp[mid] < envelopes[i][1]) { left = mid + 1; } else { right = mid -1; } } dp[left] = envelopes[i][1]; } } return len; } };
时间复杂度:O(nlogn),其中 n是数组 envelopes的长度,排序需要的时间复杂度为 O(nlogn),动态规划需要的时间复杂度同样为 O(nlogn)。
空间复杂度:O(n),即为数组dp需要的空间。
class Solution { public: int maxEnvelopes(vector<vector<int>>& envelopes) { if (envelopes.empty()) { return 0; } int n = envelopes.size(); sort(envelopes.begin(), envelopes.end(), [](const auto& e1, const auto& e2) { return e1[0] < e2[0] || (e1[0] == e2[0] && e1[1] > e2[1]); }); vector<int> f(n, 1); for (int i = 1; i < n; ++i) { for (int j = 0; j < i; ++j) { if (envelopes[j][1] < envelopes[i][1]) { f[i] = max(f[i], f[j] + 1); } } } return *max_element(f.begin(), f.end()); } };