leetcode1558题解【贪心】
- 2020 年 9 月 27 日
- 笔记
- +CodeForces, +贪心
leetcode1558.得到目标数组的最少函数调用次数
算法
贪心
时间复杂度O(nlogN)
,N
为数组中最大的那个数。
1.题意就是给定一个函数,该函数有两种功能,一种就是将数组中的所有数同乘以2,另一种就是将数组中的某个数加1。给定一个数组nums,让你将初始值全为0的数组arr通过调用给定的函数来变成nums。问最少调用次数。
2.刚开始模拟了一番,但因为考虑的方法不对(至于哪里不对呢,就是一开始我就把数组的值都加1了一遍,然后再同乘以2,最后再一个个补1,这么做显然不利于减少次数。至于当时为啥这么想,估计是脑子抽了),最终无果。
3.接下来步入正题,我们可以这么想。当初始值都为0时,我们可以使部分值首先加1(即最终要变成的较大的值),让它们首先乘以2,不断乘,乘到一定程度再处理。但这么做有个问题,让哪部分值先变成1呢?还有就是乘到哪种程度呢?
这个地方的确是个难点,不太容易想。如果直接模拟的话不太容易。
数组中谁乘2的次数最多,当然是目标值最大的那个数乘的次数最多,其他目标值较小的就相对来说乘的次数较少。我们可以贪心地让那些乘的次数较少的包含在乘的次数最多里面,至于它具体是怎么乘的,我们不用考虑太多,这就是贪心算法的好处。
如何计算每个数最终乘的次数,我们可以分情况讨论:
为了方便,我们可以从目标值向初始值变化。
-
对于奇数,可以先让其变为偶数(变为偶数这个过程即减1,这个需要单独记录),然后再除2,每除一次就记录一次。
-
对于偶数,就除2,每除一次就记录一次。可能除着除着就变成奇数了,比如250,这时候就执行上一步。
最终,数组中的值就都变成0了。
4.总之,这道题的基本思路就是求出目标数组中最大值变成0需要除2的次数,以及该数组中每个数需要减1的次数(什么时候减,就是为奇数的时候减),二者相加即为答案。
C++代码
class Solution {
public:
int minOperations(vector<int>& nums) {
int len = nums.size();
int res = 0;
int mx = 0; //记录乘以2的最大次数
for(int i = 0; i < len; i++){
int cnt = 0;
while(nums[i]){
if(nums[i] % 2 == 1){
nums[i] -= 1; //把它变成偶数
res++;
}
if(nums[i] > 0){
nums[i] /= 2;
++cnt;
}
}
mx = max(mx, cnt);
}
res += mx;
return res;
}
};