leetcode1558. 得到目标数组的最少函数调用次数(Python3、c++)

本博客介绍如何解决LeetCode第1558题,通过模拟和位运算策略找到将初始全为0的数组转换为目标数组所需的最小函数调用次数。文章详细阐述了解题思路,并提供了Python3和C++的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

leetcode1558. 得到目标数组的最少函数调用次数

给你一个与 nums 大小相同且初始值全为 0 的数组 arr ,请你调用以上函数得到整数数组 nums

请你返回将 arr 变成 nums 的最少函数调用次数。

答案保证在 32 位有符号整数以内。

示例 1:

输入:nums = [1,5]
输出:5
解释:给第二个数加 1 :[0, 0] 变成 [0, 1] (1 次操作)。
将所有数字乘以 2 :[0, 1] -> [0, 2] -> [0, 4] (2 次操作)。
给两个数字都加 1 :[0, 4] -> [1, 4] -> [1, 5] (2 次操作)。
总操作次数为:1 + 2 + 2 = 5 。

示例 2:

输入:nums = [2,2]
输出:3
解释:给两个数字都加 1 :[0, 0] -> [0, 1] -> [1, 1] (2 次操作)。
将所有数字乘以 2 : [1, 1] -> [2, 2] (1 次操作)。
总操作次数为: 2 + 1 = 3 。

示例 3:

输入:nums = [4,2,5]
输出:6
解释:(初始)[0,0,0] -> [1,0,0] -> [1,0,1] -> [2,0,2] -> [2,1,2] -> [4,2,4] -> [4,2,5] (nums 数组)。

示例 4:

输入:nums = [3,2,2,4]
输出:7

示例 5:

输入:nums = [2,4,8,16]
输出:8

提示:

  • 1 <= nums.length <= 10^5
  • 0 <= nums[i] <= 10^9

方法:模拟+位运算

思路:

分析这个函数可知,这个函数每次操作可以选择两种,一种是将数组中某个数加1,另一种是将数组中所有数×2。

因此,我们可以知道,×2的操作是针对数组中所有数字的,因此可以共享,即我们算出数组中每个数需要做乘法的次数,这些次数中最大的次数即为我们需要的乘法次数(对于那些需要乘法次数小于这个最大次数的数字,可以在这个最大数字的剩余乘法次数与它需要的乘法次数相等时,再将这个数从0变换到1再开始进行后续的乘法,这样它们可以同时达到结果)。

由于加法是对每个数字的,因此我们计算的每个数字需要的加法次数需要累加。

因此最后的答案就是总的加法次数+最大乘法次数。

对于每个数字,我们如何计算需要的乘法和加法次数呢?假设数字为n,我们应该自顶向下递归计算:

  • 如果n为偶数,那么它应该是乘2得到的,因此乘法次数+1,n=n/2,继续递归。
  • 如果n是奇数,那么它应该是加1得到的,因此加法次数+1,n=n-1,继续递归。
  • 最后算出加法和乘法的次数。

对于上面的过程,我们可以使用位运算的方法来简化。首先我们知道,对某个数乘2的操作,即为向左位移1位的操作,而二进制中的每个1,都是进行+1得到的。因此,我们可以遍历n,计算二进制中的1的个数,那么这个个数即为加法次数,最高的1在第几位,就说明经过了几次位移操作,即乘法次数。

通过位运算我们算出每个数的加法次数和乘法次数,返回加法次数和+最大乘法次数即可。

代码:

Python3:
class Solution:
    def minOperations(self, nums: List[int]) -> int:
        maxxmul = 0
        res = 0

        # count计算返回n需要的乘法和加法次数
        @lru_cache(None)
        def count(n):
            add = 0
            mul = 0
            for i in range(31):
                # 如果这一位是1,加法++,乘法数为最高位1的位数
                if n & 1<<i:
                    add += 1
                    mul = i
            return add,mul
        # 遍历所有的数,累加所有加法次数,加上最大的乘法次数                         
        for num in nums:
            add,mul = count(num)
            res += add
            maxxmul = max(maxxmul,mul)
        res += maxxmul
        return res
cpp:
class Solution {
public:
    int minOperations(vector<int>& nums) {
        int maxxmul = 0,res = 0;         
        // 遍历所有的数,累加所有加法次数,加上最大的乘法次数                         
        for (auto &num:nums){
            auto ans = count(num);
            res += ans.first;
            maxxmul = max(maxxmul,ans.second);
        }          
        res += maxxmul;
        return res;

    }
    // count计算返回n需要的乘法和加法次数
    pair<int,int> count(int n){
        int add = 0,mul = 0;
        for (int i = 0;i < 31; ++i){
            // 如果这一位是1,加法++,乘法数为最高位1的位数
            if (n & 1<<i ){
                add ++;
                mul = i;
            }               
        }          
        return {add,mul};
    }
};

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值