思路:

题目的主要信息:

  • vector中是每个信封的长度和宽度,当一个信封长度和宽度都大于另一个信封时,便可以嵌套
  • 题目要求最大嵌套数量,且长宽不能颠倒

方法一:动态规划
具体做法:
我们可以用动态规划来解决。首先对数组进行排序,将较长的信封放在前面,使之成为一个信封长度递减的序列。
维护一个辅助数组dp,表示仅使用信封下标为时的最大嵌套度。
我们遍历整个数组维护dp,对于,我们遍历其前面所有的信封,找到能放下它的最大嵌套度,并更新

class Solution {
public:
    int maxLetters(vector<vector<int> >& letters) {
        int n = letters.size();
        if(n == 0)
            return 0;
        // 优先按照长排序
        sort(letters.begin(), letters.end(), [](vector<int>& a, vector<int>& b){
            return a[0] > b[0];
        });
        vector<int> dp(n, 0); //dp[i]表示前面i个信封最多有多少嵌套
        int res = 0;
        for(int i = 0; i < n; i++){
            // 遍历该信封之前的信封,看能放下它的且最多层的个数
            int max = 0;
            for(int j = 0; j < i; j++){
                if(letters[j][0] > letters[i][0] && letters[j][1] > letters[i][1]){
                    if(max < dp[j]) // 如果可以放下当前信封,与当前最大比较
                        max = dp[j];
                }
            }
            dp[i] = max + 1;  // 遍历一圈,找到最高
            res = res > dp[i] ? res : dp[i]; // 维护答案
        }
        return res;
    }
};

复杂度分析:

  • 时间复杂度:,排序的复杂度为,两层遍历
  • 空间复杂度:,辅助数组dp

方法二:动态规划改进(二分法)
具体做法:
我们首先还是要对数组进行排序,这次是让信封长度小的放前面,相同长度是宽度大的在前。既让长度是递增序列,我们只需要在后面找到宽度递增的信封依次嵌套即可。
维护一个辅助数组dp,其中表示前个元素可以组成的长度为j的最长严格递增子序列的末尾元素的最小值。在定义范围内,可以看出dp值是严格单调递增的,因为越长的子序列的末尾元素显然越大。
后面遍历原数组,对于宽度大于结尾宽度的,我们可以直接加入dp末尾,否则我们二分法(lower_bound函数)找到第一个大于等于当前宽度的位置,因为后面的元素都是长度递增,相同情况下宽度递减,所以我们更新更小的宽度到刚刚找的位置,这样才能嵌套更多,相当于每次都把最小的信封塞到了前面,因为相同长度下,选择了宽度更小的信封。
图片说明

class Solution {
public:
    int maxLetters(vector<vector<int> >& letters) {
        int n = letters.size();
        if(n == 0)
            return 0;
        //按长度排序,不同于方法一,这次小的在前
        sort(letters.begin(), letters.end(), [](vector<int>& a, vector<int>& b){
            return a[0] < b[0];
        });
        vector<int> dp(1, letters[0][1]); //表示嵌套的信封
        int res = 0;
        for(int i = 1; i < n; i++){
            if(letters[i][1] > dp.back())
                dp.push_back(letters[i][1]); //宽度大于dp结尾,可以嵌套
            else{
                //二分查找找到第一个大于等于letters[i][1]的值
                auto iter = lower_bound(dp.begin(), dp.end(), letters[i][1]);
                *iter = letters[i][1];
            }
        }
        return dp.size();
    }
};

复杂度分析:

  • 时间复杂度:,排序时间是,一次遍历,遍历中二分查找为
  • 空间复杂度:,辅助数组dp