大概题意:给定一个已经排好序的数组,还有一个数n,问你还需要往数组中添加多少个数,才能对于1 到 n 的每一个数a,都存在数组中的若干个数,使得它们的和等于a 。
这道题我并没有单独完成,我思考了很久,花了挺长的时间进行尝试,依然没有通过这道题。
一开始困扰我的问题是,怎么判断当前数组里的数是否已经满足了所要的条件呢?假如这里面有m个数,如果我用穷举法来检验,那时间复杂度就是O(2的n次方)。显然太大了,不可能用在这道题里面。就算数据量足够小,要找出要添加几个数还是很困难的。
之后我发现了,1, 2, 4, 8, ... , 2的m次方(n <= 2 的m次方),能够组成所有1 到 n中的数字。至于为什么,想一下每个数字的二进制表示就很容易清楚了。而这样也是最少的(当然,在不考虑原数组元素的情况下)。于是我在这样的思路下,在原数组中插入数字来补充 1 到 n 的缝隙(即原来的数字不能组成的数)。这样的做法能够通过一些测试样例,但不能通过这道题。
最后我还是查找了题解。具体的做法也很简单:设当前能够组成的数为1 ~ curMax。每一步主要考虑着两个问题:现在是否应该取剩余数组中的数?如果不应该,又应该往数组里添加什么数字呢?
关于第一个问题,设剩余数组元素中的最小值为s,如果s大于curMax + 1,那我们就不能取,因为curMax + 1 ~ s - 1 这一段我们无法通过所拥有的元素组合得到。也就是说,当s 小于等于curMax 的时候,我们应该取。
第二个问题,应该往数组里添加curMax + 1,这样1 ~ 2*curMax + 1 的数我们都能得到了。
我从这个题的做法中得到了一些启发:我做这道题的时候,主要的思路是“我该往数组中添加多少数字、什么数字”;但题解的思路是“什么时候可以用到这些数组中的数字”。方向可以说是完全不相同的。所以说,以后做题的时候,特别是对这种贪心类型的题目,换一个方向去思考也许会有不同的收获。
代码如下,设数组大小为m,时间复杂度在最糟糕的情况下为O(m + logn)。注意curMax 可能会超出int的数据范围。
class Solution {
public:
int minPatches(vector<int>& nums, int n) {
long long curMax = 0 ;
int curNum = 0 ;
int cnt = 0 ;
int p = 0 ;
int s = nums.size() ;
while (curMax < n) {
if (p < s && nums[p] <= curMax + 1) {
curMax += nums[p] ; p++ ;
}
else {
curMax += curMax + 1 ;
cnt++ ;
}
}
return cnt ;
}
};