330. Patching Array(Hard)

本文介绍了一种通过向已排序的正整数数组添加元素,确保所有[1,n]范围内的数值均可由数组元素组合得到的算法。使用贪心策略,实现O(n)的时间复杂度。

原题目:
  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.

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.

题目大意如下:
  给定一个从小到大排好序正整数数组nums和一个整数n,要求在这些数组中补充一些元素,使得[1,n]这个区间的所有数都能由数组中的部分元素求和得到。返回需要添加的最少的元素个数。

解题思路:
  这个问题问的是“the minimum number of patches required”也就是要求出一个最优解,一般涉及到最优解的问题都可以用动态规划或者贪心算法解决,这里我们可以先用贪心算法。那理所当然的下一步,就是寻找最优子结构——子问题的最优解。
  举个例子,nums=[1,2,3,9],既然这个数组已经是排好序的,很自然我们会想到从左到右遍历这个数组看看我们会有什么发现。
  遇到nums[0] = 1,那么[1]这个区间的所有数可以得到;
  遇到nums[1] = 2,那么[1,2,3]这个区间的所有数可以得到;
  遇到nums[2] = 3,那么[1,2,3,4,5,6]这个区间的所有数可以得到;
  遇到nums[4] = 9,那我们只能得到[1,2,3,4,5,6 , 9,10…15]这个区间内的所有数,显然我们需要7和8。
  要得到7,我们可以从[1,2,3,4,5,6,7]当中任意挑选一个数,那哪个是最优选择呢?显然,最能让我们快速到达结果的那个就是最优,即最大的那个数——7(也是原来区间当中最大的数加一)。当我们选择7之后(从遇到nums[2]往后遍历),我们可以得到[1…13]这个区间里的所有数。最后遇到nums[4] = 9我们 [1…21]这个区间里的所有数。
  最后,当区间里最大的数小于n时,重复按照以上标准添加元素即可。
  
注意:
  对于区间的扩展,如果我们可以得到1到n的所有数,当我们拿进来n+1,那我们可以得到1到n+(n+1)所有数。例如,[1,2,3,4,5,6],如果我们有7则可以得到[1,2,3,4,5,6,7,8,9,10,11,12,13]——把7对应每个元素相加即可。
  
代码如下:

class Solution {
public:
    int minPatches(vector<int>& nums, int n) {
        //sum到最后数字可能会变得特别大
        long sum = 0 , ans = 0 ;
        //如果区间为空,一直添加最大值加一
        if(nums.empty()){
            while(sum < n){
                sum += sum +1 ;
                ans++ ;
            }
            return ans ;
        }
        for(int i = 0 ; i < nums.size() ; ++i){
            while(nums[i] > sum + 1){
                sum += sum +1 ;
                ans++ ;
                //每次添加后判断是否满足,避免多余添加
                if(sum >= n) return ans ;
            }
            sum += nums[i] ;
            if(sum >= n) return ans ;
        }
        //如果原来的区间遍历完以后还没有大于n
        while(sum < n){
            sum += sum +1 ;
            ans++ ;
        }
        return ans ;
    }
};

运行结果:
运行结果

算法分析:
  最主要的是便利整个数组,所以为O(n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值