题目:
给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (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