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