美团面试手撕 —— 往集合S中加入最少的数,使得集合T达到要求,那些我当时被冲昏了头脑的题

在这里插入图片描述
给定一个集合S, S到集合T的映射是S中的元素任意组合之和构成的集合。现在可以往S中加一些数,使得集合T能覆盖1至N,求加数的最少的个数。

分析:

1. 先通过一个例子来体会其中的规律

假设S = {2, 3, 5},首先1必须加进去,1的加入使得集合变成了S1 = {1, 2, 3, 5},此时的T = {1,2,3,4,5,6,7,8,9,10,11},即T的区间为[1, 11],此时还不能达到目标。若想达到[1, 20],再往其中加任意一个大于位于区间[9, 12]的数都可以达到目标【因为小于9无法达到20,大于12又会出现11后面的断层】。其实可以发现,假设S映射到T集合的区间为[1, m],若此时我们再加入一个数a,则能够拥有新区间[a, a+m]。为了在不出现断层的情况下使得新合并区间最大,我们应该贪心的加入a=m+1,此时新区间为[1, 2*m+1],其实一旦a<m+1,就会有一段区间重合,这是非常低效的,所以我们避开重合,而尽量去扩展区间。这种贪心思想就是这道题目的精髓。

2. 更多细节

由于原始数组中已经存在一些数,所以在并非每一次区间更新都是由于我们主动加入新数。区间更新还有可能由数组中已经存在的数导致。举个例子,S = {1, 2, 4, 6},在我们从左往右遍历的过程中,{1, 2, 4}能够产生T∈[1,7],而位于该区间内的 6 的加入,会更新T∈[1, 13]。这样看来,我们只需要维护一个区间的右界right,一旦有界内的数在nums里面,我们就可以去更新右界right了。直到遍历到right+1,说明当前集合S只能使得T最大为right了,若想扩展,则必须往S中加入新数,根据之前的分析,我们贪心的加入num=right+1,同时更新区间右界为2*right+1。

nums = [1, 3, 10]
target = 23
right = 0
cnt = 0
i = 0
add = []
while right < target:
    while right < target and i <= right:
        i += 1
        if i in nums:
            right += i
    if right >= target:
        break
    cnt += 1
    right += i
    add.append(i)
print(cnt)
print(add)

特殊情况:

当nums为空时,我们会发现每次加入的数为1,2,4,8,……,2**k,所以k=log2 N 向上取整。也可以用二分法去找到一个临界的k值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值