354.俄罗斯套娃信封问题(困难)

题目:
给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h) 出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。

说明:
不允许旋转信封。

示例:
输入: envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出: 3
解释: 最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。

题目链接:
https://leetcode-cn.com/problems/russian-doll-envelopes

解题思路:
使用动态规划
1.思路
信封a装进信封b的条件是,a的宽和高必须都小于b
将信封按照宽w从小到大排序后,便只需考虑信封的宽不同并且后面信封的高h大于前面信封即可
优化:由于宽和高都是按照从小到大的顺序排列,在遍历高h时,可能两者宽w相等,后面信封高大于前者需要另外判断,比较繁琐,所以 可以将高h按照从大到小的顺序排列,便无需再判断两者的宽w是否相等,从而转化为了一维的最长上升子序列问题。

2.复杂度分析
时间复杂度:O(N^2)
空间复杂度:O(N)

代码:

class Solution:
    def maxEnvelopes(self, envelopes):
        if not envelopes:
            return 0
        N = len(envelopes)
        envelopes.sort((key=lambda x:(x[0],-x[1]))
        print(envelopes)
        res = 0
        dp = [1] * N
        for i in range(N):
            for j in range(i):
                if envelopes[j][0] < envelopes[i][0] and envelopes[j][1] < envelopes[i][1]:
                    dp[i] = max(dp[i], dp[j] + 1)
                print('i,j,dp',i,j,dp)
        print(dp)
        return max(dp)
s = Solution()
print(s.maxEnvelopes([[2,100],[3,200],[4,300],[5,500],[5,400],[5,250],[6,370],[6,360],[7,380]]))

在这里插入图片描述

方法二:使用二分法
1.思路
最长上升子序列可以通过二分法来优化,即定义一个数组arr来存储遍历过的上升子序列,
例如数组为[2,5,3,4]
当遍历到2时,数组arr为空,加入首元素变为[2]
当遍历到5时,arr中存储的数为[2,5]
当遍历到3时,target=3,在arr中二分查找可插入的位置index,此位置上的数arr[index]>=taget,将index位置上的数替换为target,arr= [2,3],而后面的数num可能出现target<=num<=arr[index],使得子序列更长
当遍历到4时,arr变为[2,3,4]

2.复杂度分析
时间复杂度:O(NlogN)
空间复杂度:O(N)

作者:yim-6
链接:https://leetcode-cn.com/problems/russian-doll-envelopes/solution/python3-dong-tai-gui-hua-fa-er-fen-fa-by-yim-6/

代码一:

import bisect
class Solution:
    def maxEnvelopes(self, envelopes):
        if not envelopes:return 0
        envelopes.sort(key=lambda x:(x[0],-x[1]))
        n=len(envelopes)
        stack=[]
        for i in range(n):
            h=envelopes[i][1]            
            if not stack or stack[-1]<h:
                stack.append(h)
            else:
                index=bisect.bisect_left(stack,h)
                stack[index]=h
        return len(stack)  

s = Solution()
print(s.maxEnvelopes([[2,100],[3,200],[4,300],[5,500],[5,400],[5,250],[6,370],[6,360],[7,380]]))

代码二:

class Solution:
    def maxEnvelopes(self, envelopes):
        if not envelopes:
            return 0
        n=len(envelopes)
        envelopes.sort(key=lambda x:(x[0],-x[1]))
        arr=[envelopes[0][1]]

        def bin_search(target):
            l,r=0,len(arr)-1
            while l<=r:
                mid=l+(r-l)//2
                if arr[mid]==target:
                    return mid
                elif arr[mid]<target:
                    l=mid+1
                else:
                    r=mid-1
            return l                       

        for i in range(1,n):
            w,h=envelopes[i]
            index = bin_search(h)
            if index==len(arr):
                arr.append(h)
            else:
                arr[index]=h
        return len(arr)

s = Solution()
print(s.maxEnvelopes([[2,100],[3,200],[4,300],[5,500],[5,400],[5,250],[6,370],[6,360],[7,380]]))

知识点:二分查找模块bisect

bisect 模块包含两个主要函数, bisect 和 insort两个函数都利用二分查找算法来在有序序列中查找或插入元素。
bisect 查找
bisect(haystack,needle)在haystack(干草垛)里搜索 needle(针)的位置,该位置满足的条件是,把 needle 插入这个位置之后, haystack 还能保持升序。也就是在说这个函数返回的位置前面的值,都小于或等于 needle 的值。

>>> import bisect
>>> a = [1,4,7,13]
>>> a1 = bisect.bisect(a,6) #在列表a中插入6
>>> a1 #6在列表a中插入的位置
2

bisect 函数其实是 bisect_right 函数的别名,后者还有个姊妹函数叫bisect_left。
bisect_left函数是新元素会被放置于它相等的元素的前面,而 bisect_right返回的则是跟它相等的元素之后的位置。

>>> a
[1, 4, 7, 13]
>>> bisect.bisect(a,7)
3
>>> bisect.bisect_left(a,7)
2

bisect 可以用来建立一个用数字作为索引的查询表格:

def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
    i = bisect.bisect(breakpoints, score)
    return grades[i]

#['F', 'A', 'C', 'C', 'B', 'A', 'A']
print([grade(score) for score in [33, 99, 77, 70, 89, 90, 100]])

对于33来说,通过bisect返回的位置是0,所以对应的grades值是[0]=F。
对于99来说,通过bitsect返回的位置是3,所以对应的grades值是[3]=B。

insort 插入
insort(seq, item) 把变量 item 插入到序列 seq 中,并能保持 seq 的升序顺序。

import random
import bisect

my_list = []
for i in range(10):
    new_item = random.randrange(20)
    bisect.insort(my_list, new_item)
print(my_list)
————————————————
版权声明:本文为CSDN博主「zhexiao27」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/andybegin/article/details/84765049
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值