题目描述
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.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]范围内的所有数都能由nums
中的一个或一些数通过求和得到。求在数组中加入数字的最小数量。
算法分析
要解决这道题目,首先明白一个规律:假设当前我们通过数组的前i
个数,已经能通过求和覆盖[1, m]
内的所有值,那么当我们再利用数组的第i+1
个数,即nums[i+1]
,能得到的范围是什么呢?答案是[1, nums[i+1] + m]
。在有了nums[i+1]
的基础上,分别加上[1, m]
内的所有值即可。
代码实现
为了更好理解本题的解法,先给出代码实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Solution {
public:
int minPatches(vector<int>& nums, int n) {
long missNum = 1, index = 0, toAddN = 0;
while (missNum <= n) {
if (index < nums.size() && nums[index] <= missNum) {
missNum += nums[index];
index++;
}
else {
toAddN++;
missNum *= 2;
}
}
return toAddN;
}
};
其中,missNum
为代表当前缺失的值,index
代表当前数组下标,toAddN
表示要加上数的最小的数量。
算法核心思想如下:假设当前我们已经通过nums[0]
到nums[index-1]
中的所有数,获得了范围在 [1, missNum) 的所有值。那么,当我们把下标index-1
移到下一位index
,将会出现两种情况:
nums[index] <= missNum
。那么,根据我们之前得到的规律,现在可得的的范围,已经可以扩展到[1, missNum + nums[index])。nums[index] > missNum
。那么我们已经无法通过数组中的和得到missNum
这个值了,于是我们需要把这个值加入到数组中,且toAddN
计数加一。此时,和的范围已经变成了[1, missNum * 2)。
根据以上规律,我们每扫过nums
中的一个数,便能得到一个新的和的范围。当范围值已经覆盖到输入的n
的时候,证明添加的数已经得到了要求。
可是,如何证明按照这个算法得到的toAddN
一定是最小的呢?原理很简单,当需要在数组中添加一个数时,添加的数越大,和的范围就会相应地扩展到最大。又由于缺失的missNum
是无法由前面的数求和得到的,那么此时将missNum
添加到数组中,就是能同时满足这两个条件的值。因此添加missNum
是最优的做法。
本文为博主原创文章,转载请注明出处。