Leetcode-330. Patching Array

问题描述:

  • Given a sorted positive integer array nums and an integer n, add/patch elements to the array such that any number in range [1, n] inclusive can be formed by the sum of some elements in the array. Return the minimum number of patches required.
  • 给你有序的正数数组nums, 和正数n。 问, 如果你可以自由选择nums中的数, 想加出1~n上的任何一个数, 你还缺几个数。
  • Example 1

nums = [1, 3], n = 6
Return 1.
Combinations of nums are [1], [3], [1,3], which form possible sums of: 1, 3, 4.
Now if we add/patch 2 to nums, the combinations are: [1], [2], [3], [1,3], [2,3], [1,2,3].
Possible sums are 1, 2, 3, 4, 5, 6, which now covers the range [1, 6].
So we only need 1 patch.

  • Example 2

nums = [1, 5, 10], n = 20
Return 2.
The two patches can be [2, 4].

  • Example 3

nums = [1, 2, 2], n = 5
Return 0.

思路分析

  • 首先这题是求最小值,既是一个最优问题。一般来说,最优问题都可以用动态规划或者贪心算法来解决。该题用贪心算法。
  • 定义一个变量range,表示当前可以达到的最大范围,也便是用数组(0~i-1)位置上的元素能够加出来(1~range)范围内所有的数,range便是表示该范围的最大右边界。所以初始化range为0,此时还没有计算数组中的任何一个数。
  • 大思路从左到右遍历数组,不断更新range,直至range>n,结束遍历。那么如何更新(迭代)range以及如何为数组打补丁呢(添加数)?
    • 若遍历完(0~i-1)位置,用数组(0~i-1)位置上的元素能够加出来(1~range)范围内所有的数,range表示该范围的最大右边界。
      那么如果cur[i] > range+1,则无法用nums[0~i]加出来 range+1,则出现了断点,此时必须打一个 range+1的补丁,即向数组中添加 range+1这个数,以此保证范围的连续性。此时range更新为 range +=range + 1,继续检查cur[i]与range+1的大小。 如果cur[i] <= range+1,那么此时用nums[0~i]可以加出来 (1~cur[i]+range)内所有的数,则此刻无需打补丁,range更新为cur[i]+range。继续向右遍历。
    • 举例说明,现有数组[1,2,3,9],range初始值为0,对于1而言,range被更新为了1。向右遍历,对于2而言,range被更新为了3,用[1,2]能够加出来1,2,3。
      对于3而言,可以这样考虑,3与(1~range)相加,那么可以加出(4~range+3)也就是(4~6),而先前范围为(1~3),那么这两个区间可以无间隔地合并在一起,说明用[1,2,3]可以加出来(1~6),此时range更新为了6。
      但对于9,9与(1~range)相加为(10~15),与原范围(1~6)有间隔,无法合并,7,8,9这三个值无法加出来,说明此刻必须打补丁。
    • 那么如何判断需不需要打补丁以及如何打补丁呢?
      原范围为(1~range),与cur[i]相加后形成(1+cur[i],range+cur[i]),并且cur[i]也可不与其他数相加,即只选这一个值,所以区间被扩(cur[i],range+cur[i]);为了保证两个区间可以无间隔合并为一个区间,必须要求cur[i] <= range+1
      如果cur[i] > range+1,必须打补丁,保证区间连续,补充的值便是第一个断点位置,range+1,你可以打小于range+1大于0的数作为补丁,但是range+1是既能修补断点,又能保证打了补丁后,range能更新到最大的一个值,这样便能使补丁数目最小。这便是贪心算法的应用之处,每次的选择都是最优的
    • 注意,可能数组全部遍历结束,还没有加到n,那么每次都打range+1的补丁,不断迭代更新range,直到range>n

经验教训

  • 一般来说,最优问题都可以用动态规划或者贪心算法来解决。
  • 该题range的概念与用法与另一道题的进阶问题类似,附上链接:正数数组的最小不可组成和问题
  • 深刻理解range的用法
  • range可能会是数组中所有数的累加和,为了防止溢出,设为long

代码实现

  • 方法一
public static int minPatches(int[] nums, int n) {
        int patchesNum = 0;
        long range = 0;
        //遍历数组
        for (int i = 0; i < nums.length; i++) {
            //range+1不可达:打一个range + 1的补丁,更新补丁数目,打补丁到直至可达
            while (nums[i] > range + 1) {
                patchesNum++;
                range += range + 1;
                //检查是否到n
                if (range >= n) {
                    return patchesNum;
                }
            }
            //无需打补丁,直接更新range,并检查是否到n
            range += nums[i];
            if (range >= n) {
                return patchesNum;
            }
        }
        //数组遍历结束还没有加到n,
        //每次需要打一个range + 1的补丁,更新补丁数目,更新range,直至range >= n
        while (range < n) {
            patchesNum++;
            range += range+1;
        }
        return patchesNum;
    }
  • 方法二
public static int minPatches(int[] nums, int n) {
        int patchesNum = 0;
        long range = 0;
        int index = 0;
        while(range < n) {
            //数组尚未遍历完,并且nums[index] <= range+1:无需打补丁,直接更新range
            if (index < nums.length && nums[index] <= range+1) {
                range += nums[index];
                index++;
            }else {//要么是nums[index] > range+1,导致range+1不可达;要么是数组已经遍历结束还没有到达n
                //都需要打一个range + 1的补丁,更新补丁数目,更新range
                patchesNum++;
                range += range + 1;
            }
        }
        return patchesNum;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值