俄罗斯套娃信封问题 是一个二维优化问题,类似于经典的 最长递增子序列(LIS) 问题。给定若干个信封,每个信封具有两个维度:宽度 w 和高度 h。要求找出最多可以嵌套的信封个数。一个信封 A 可以嵌套到信封 B 中的条件是:A.w < B.w 且 A.h < B.h。
通过将问题从二维降到一维,并结合动态规划和二分查找的思想,我们可以有效地解决这个问题。本文将详细讲解这一思路,并提供 Python 和 C++ 的实现。

给定一组信封,每个信封具有宽度 w 和高度 h。要求找到最多可以嵌套的信封个数。信封 A 可以嵌套在信封 B 中的条件是:
这个问题可以理解为一个二维的优化问题,通过某种方式排序或优化后,问题可以简化为经典的一维 最长递增子序列(LIS) 问题。
我们可以通过 排序 将问题从二维降到一维:
w 进行升序排序。h 降序排序。这样做可以确保同宽度的信封不会被错误地嵌套。h 上寻找 最长递增子序列(LIS)。在已经排序的信封序列中,问题变成了在信封高度 h 上寻找最长递增子序列。定义 dp[i] 为以信封 i 结尾的最长嵌套序列的长度。对于每个信封 i,我们可以通过前面的信封来更新 dp[i]:
其中 envelope_j.h 和 envelope_i.h 分别是信封 j 和 i 的高度。
我们可以将动态规划求解 LIS 的时间复杂度优化为 O(n log n),通过二分查找来维护一个数组 dp,该数组动态记录了当前找到的最长递增子序列:
dp 数组中找到信封的高度 h 应插入的位置。h 可以替换 dp 数组中的某个元素,就进行替换,否则将 h 追加到 dp 数组末尾。dp 数组的长度即为最长递增子序列的长度。sort envelopes by width ascending and by height descending if width is the same
initialize dp array for LIS on heights
for each envelope in envelopes:
perform LIS on heights using dynamic programming or binary search
return length of the longest increasing subsequence in heightsw 升序排序,若宽度相同则按高度 h 降序排序。h,问题变为 LIS 问题,使用 dp 数组维护最长递增子序列。O(n^2) 优化为 O(n log n)。bisect_left(Python)或 lower_bound(C++)来找到合适的位置并更新 dp 数组。通过结合二分查找的方式,时间复杂度可以优化为 O(n log n),适用于大规模数据。
O(n log n) 时间内找到最优解。以上就是俄罗斯套娃信封问题问题的基本思路。
class Solution:
def maxEnvelopes(self, envelopes: list[list[int]]) -> int:
# 先对信封按宽度升序排序;如果宽度相同,则按高度降序排序
envelopes.sort(key=lambda x: (x[0], -x[1]))
# 只需要在高度数组上寻找LIS
heights = [envelope[1] for envelope in envelopes]
# tails数组用于存储LIS
dp = []
for h in heights:
idx = bisect_left(dp, h) # 找到 h 在dp中应插入的位置
if idx < len(dp):
dp[idx] = h # 替换当前位置的值
else:
dp.append(h) # 如果找不到合适的位置,直接追加
return len(dp) # dp数组的长度即为最长递增子序列的长度bisect_left 在 dp 数组中找到当前高度 h 应插入的位置,并动态维护 dp 数组。dp 数组的长度即为最长递增子序列的长度。class Solution {
public:
int maxEnvelopes(vector<vector<int>>& envelopes) {
// 对信封进行排序,宽度升序排列,宽度相同时高度降序排列
sort(envelopes.begin(), envelopes.end(), [](const vector<int>& a, const vector<int>& b) {
return a[0] == b[0] ? a[1] > b[1] : a[0] < b[0];
});
// 提取高度
vector<int> heights;
for (const auto& envelope : envelopes) {
heights.push_back(envelope[1]);
}
// 使用动态规划+二分查找计算LIS
vector<int> dp;
for (int h : heights) {
auto it = lower_bound(dp.begin(), dp.end(), h); // 找到h应该插入的位置
if (it != dp.end()) {
*it = h; // 替换当前值
} else {
dp.push_back(h); // 如果没有合适的位置,追加h
}
}
return dp.size(); // dp数组的长度即为最长递增子序列的长度
}
};lower_bound 查找应插入 dp 数组中的位置,动态维护 dp 数组,记录最长递增子序列的长度。dp 数组的长度即为最大嵌套信封的数量。O(n^2) 优化到 O(n log n),从而可以高效处理大规模数据。