原题指路
题目描述
给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h)
出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
说明:
不允许旋转信封。
解题思路
困难题就直接参考一波官方题解……
- 首先,在套娃前进行准备工作,对信封按照第一维 w w w进行升序排列,先得到一个 w w w的递增序列;
- 其次,为了防止后面迭代过程中出错,即保证当信封的 w w w相等时,仅会在一个序列里选中其中一个信封,所以将信封的第二维 h h h作为次关键字将序列按降序排列(与第1步合并为以 w w w为主关键字,以 h h h为次关键字的快排);
- 在排序完成后,就可以开始套娃(bushi)动态规划了,用 d p [ i ] dp[i] dp[i]表示当序列长度为 i i i时最外面一层信封的 h h h;
- 动态规划的步骤具体可以用二分实现(易证迭代过程中dp为严格单调递增序列):
- 若当前信封的 h i > d p [ − 1 ] h_{i} > dp[-1] hi>dp[−1]时,则 d p [ l e n ( d p ) + 1 ] = h i dp[len(dp)+1]=h_{i} dp[len(dp)+1]=hi;
- 否则就二分求出 i n d e x index index使 d p [ i n d e x ] ≤ h i < d p [ i n d e x + 1 ] dp[index] \le h_{i} < dp[index+1] dp[index]≤hi<dp[index+1],且使 d p [ i n d e x ] = h i dp[index] = h_{i} dp[index]=hi。
- 最后返回 l e n ( d p ) len(dp) len(dp)即可。
时间复杂度:
O
(
n
log
n
)
O(n\log{n})
O(nlogn)(注:快排和基于二分的动态规划均为
O
(
n
log
n
)
O(n\log{n})
O(nlogn))
空间复杂度:
O
(
n
)
O(n)
O(n)
代码
class Solution:
def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
if not envelopes: # 空列表直接返回
return 0
length = len(envelopes)
envelopes.sort(key=lambda x: (x[0], -x[1])) # 按第一维升序,第二位降序对信封进行快排
dp = [envelopes[0][1]]
for i in range(1, length):
if (num := envelopes[i][1]) > dp[-1]:
dp.append(num)
else: # 二分查找部分
index = bisect.bisect_left(dp, num)
dp[index] = num
return len(dp)