330.按要求补全数组

  • 给定一个已排序的正整数数组 nums,和一个正整数n 。
  • 从[1, n]区间内选取任意个数字补充到nums中,使得[1, n]区间内的任何数字都可以用nums中某几个数字的和来表示。
  • 请输出满足上述要求的最少需要补充的数字个数。
  • 示例1:
  • 输入: nums = [1,3], n = 6
  • 输出: 1
  • 解释:
  • 根据 nums里现有的组合[1], [3], [1,3],可以得出1, 3, 4。
  • 现在如果我们将2添加到nums 中,组合变为: [1], [2], [3], [1,3], [2,3], [1,2,3]。
  • 其和可以表示数字1, 2, 3, 4, 5, 6,能够覆盖[1, 6]区间里所有的数。
  • 所以我们最少需要添加一个数字。
  • 示例 2:
  • 输入: nums = [1,5,10], n = 20
  • 输出: 2
  • 解释: 我们需要添加[2, 4]。
  • 示例3:
  • 输入: nums = [1,2,2], n = 5
  • 输出: 0

这道题之所以能递推解决,是因为对于一个数(比如197),如果它尚未被比它小的数求和得到,那么必须将197添加到原始数组里,否则以后永远得不到197了。
递推方法:我们这样模拟 [0,1) -> [0,2) -> [0,4) -> [0,8) ----> [0,y),直到 y >= n
因此当 nums(i) ∈ [0,y] 时,可取之为 k 并扩大值域, 否则视为插入一个数 y。
保存一个“目前能通过求和获得的最大数”,命名为maxnext。
一开始maxnext = 0,目标是maxnext>= n。
状态转移(动态扩展问题)
1.如果nums还没用完,下一个数是x
在没有用到x的情况下就能获得1–maxnext,那么用上x,就能获得1+x–maxnext+x了。
如果maxnext和1+x之间没有其他数,那么使用x。将1–maxnext和1+x–maxGot+x连起来,就能获得1–maxnext+x。
如果maxnext和1+x之间还有其他数,比如maxnext+1,那么不使用x,而是额外添加一个数字(maxnext+1)。将1–maxnext、maxnext+1、1+(maxnext+1)–maxnext+(maxnext+1)连起来,获得了1–(maxnext * 2 + 1)。
2.如果nums已经用完
如果maxnext + 1 <= n,就额外添加一个数字(maxnext+1),和上面情况一样。
核心逻辑:[0,x) 的若干个数加上 k,可扩大值域至 [0,x+k),注意 k∈[0,x]。
将 k 视为 nums已有的数 或者 要插入的数
显然从 [0,1) 开始模拟扩展,一般地,我们取 k 最大,也就是 k = x
假设 nums 空,我们这样模拟 [0,1) -> [0,2) -> [0,4) -> [0,8) ----> [0,y),直到 y > n
因此当 nums(i) ∈ [0,y] 时,可取之为 k 并扩大值域, 否则视为插入一个数 y。

public class model14 {
    public static void main(String[] args) {
        Test330 test330 = new Test330();
        System.out.println(test330.minPatches(new int[]{1, 3}, 6));
    }
}
class Test330 {
    public int minPatches(int[] nums, int n) {
        //用int可能会因为超出范围溢出
        long maxnext=0;
        //nums数组下标
        int i=0;
        //添加数字个数计数
        int count=0;
        //当现有值域范围右边界小于正整数n(n为要求的右边界)时循环
        while(maxnext<n) {
            //如果数组中的某个数减1小于等于现有右边界说明可以包含(例如[1,4]和[5,7],即两个区域没有断点)
            if (i < nums.length && nums[i] <= maxnext + 1) {
                //则把现有右边界加上现在的nums[i]作为新的右边界
                maxnext=maxnext+nums[i];
                i++;
            }
            //否则的话证明两个值域之间有断点,需要插入maxtnext+1,则现有右边界变为maxnext*2+1,原因查看上面解释
            //因为对于一个数,如果它尚未被比它小的数求和得到,那么必须将该数添加到原始数组里,否则以后永远得不到了
            else{
                maxnext=maxnext*2+1;
                count++;
            }
        }
        return count ;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值