思路:
题目的主要信息:
- 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

京公网安备 11010502036488号