数组元素最小操作次数问题 | 豆包MarsCode AI刷题

98 阅读3分钟

问题描述

小U有一个数组,她可以通过选择一个元素并将其除以2(向下取整)的操作来改变数组。小U想知道,最少需要多少次这样的操作才能使得所有数组元素相等。


测试样例

样例1:

输入:n = 4 ,a = [1, 2, 1, 3]
输出:2

样例2:

输入:n = 1 ,a = [114514]
输出:0

样例3:

输入:n = 5 ,a = [16, 8, 4, 2, 1]
输出:10

问题理解

你需要找到一个最小的操作次数,使得数组中的所有元素相等。每次操作可以将任意一个元素除以2(向下取整)。

数据结构选择

  • 数组 a 存储了所有的元素。
  • 我们可以使用一个哈希表(或数组)来记录每个元素出现的次数,这样可以方便地统计每个元素及其操作次数。

算法步骤

  1. 统计每个元素的出现次数:遍历数组 a,记录每个元素的出现次数。
  2. 计算每个元素的操作次数:对于每个元素,计算将其变为某个目标值所需的操作次数。目标值可以是数组中的任意一个元素。
  3. 选择最优目标值:遍历所有可能的目标值,计算总操作次数,选择操作次数最小的目标值。

具体步骤

  1. 初始化:创建一个哈希表(或数组)来记录每个元素的出现次数。
  2. 统计出现次数:遍历数组 a,记录每个元素的出现次数。
  3. 计算操作次数
    • 对于每个元素 x,计算将其变为目标值 target 所需的操作次数。
    • 操作次数为 count(x) * (number of divisions needed to make x equal to target)
  4. 选择最优目标值:遍历所有可能的目标值,计算总操作次数,选择操作次数最小的目标值。

优化思路

  1. 预处理所有可能的目标值

    • 对于每个元素,我们可以生成所有可能的目标值,直到元素变为1。
    • 使用一个集合来存储这些目标值,并统计每个目标值的出现次数。
  2. 计算操作次数

    • 对于每个目标值,计算将其变为目标值所需的操作次数。
    • 使用缓存来存储每个元素变为目标值所需的操作次数,以减少重复计算。

代码实现

#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <climits>

int solution(int n, std::vector<int> a) {
    // 创建一个哈希表来记录每个元素的出现次数
    std::unordered_map<int, int> countMap;
    for (int num : a) {
        countMap[num]++;
    }

    // 预处理所有可能的目标值
    std::unordered_set<int> possibleTargets;
    for (auto& [num, count] : countMap) {
        int currentNum = num;
        while (currentNum > 0) {
            possibleTargets.insert(currentNum);
            currentNum /= 2;
        }
    }

    // 计算每个元素变为目标值所需的操作次数
    int minOperations = INT_MAX;
    for (int target : possibleTargets) {
        int operations = 0;
        bool validTarget = true;
        for (auto& [num, numCount] : countMap) {
            // 计算将 num 变为 target 所需的操作次数
            int currentNum = num;
            int tempOperations = 0;
            while (currentNum > target) {
                currentNum /= 2;
                tempOperations += numCount;
            }
            // 如果 currentNum 不等于 target,则需要继续操作
            if (currentNum != target) {
                validTarget = false; // 标记为不可达
                break;
            }
            operations += tempOperations;
        }
        // 更新最小操作次数
        if (validTarget && operations < minOperations) {
            minOperations = operations;
        }
    }

    return minOperations;
}

int main() {
    std::cout << (solution(4, {1, 2, 1, 3}) == 2) << std::endl;
    std::cout << (solution(1, {114514}) == 0) << std::endl;
    std::cout << (solution(5, {16, 8, 4, 2, 1}) == 10) << std::endl;
}

关键步骤解释

  1. 预处理所有可能的目标值

    std::unordered_set<int> possibleTargets;
    for (auto& [num, count] : countMap) {
        int currentNum = num;
        while (currentNum > 0) {
            possibleTargets.insert(currentNum);
            currentNum /= 2;
        }
    }
    
  2. 计算每个元素变为目标值所需的操作次数

    int minOperations = INT_MAX;
    for (int target : possibleTargets) {
        int operations = 0;
        bool validTarget = true;
        for (auto& [num, numCount] : countMap) {
            int currentNum = num;
            int tempOperations = 0;
            while (currentNum > target) {
                currentNum /= 2;
                tempOperations += numCount;
            }
            if (currentNum != target) {
                validTarget = false; // 标记为不可达
                break;
            }
            operations += tempOperations;
        }
        if (validTarget && operations < minOperations) {
            minOperations = operations;
        }
    }