动态规划—俄罗斯套娃信封问题(leetcode 354)

题目描述

给你一个二维整数数组 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(nlog⁡n),其中 n是数组 envelopes的长度,排序需要的时间复杂度为 O(nlog⁡n),动态规划需要的时间复杂度同样为 O(nlog⁡n)。

空间复杂度:O(n),即为数组dp需要的空间。

 O(n^2)普通解法

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());
    }
};

上一篇:洛谷 P1001 A+B Problem


下一篇:蓝桥杯 数组求和 C++算法提高 HERODING的蓝桥杯之路