leetcode1558题解【贪心】

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;
    }
};

思路来源