问题描述
小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存储了所有的元素。 - 我们可以使用一个哈希表(或数组)来记录每个元素出现的次数,这样可以方便地统计每个元素及其操作次数。
算法步骤
- 统计每个元素的出现次数:遍历数组
a,记录每个元素的出现次数。 - 计算每个元素的操作次数:对于每个元素,计算将其变为某个目标值所需的操作次数。目标值可以是数组中的任意一个元素。
- 选择最优目标值:遍历所有可能的目标值,计算总操作次数,选择操作次数最小的目标值。
具体步骤
- 初始化:创建一个哈希表(或数组)来记录每个元素的出现次数。
- 统计出现次数:遍历数组
a,记录每个元素的出现次数。 - 计算操作次数:
- 对于每个元素
x,计算将其变为目标值target所需的操作次数。 - 操作次数为
count(x) * (number of divisions needed to make x equal to target)。
- 对于每个元素
- 选择最优目标值:遍历所有可能的目标值,计算总操作次数,选择操作次数最小的目标值。
优化思路
-
预处理所有可能的目标值:
- 对于每个元素,我们可以生成所有可能的目标值,直到元素变为1。
- 使用一个集合来存储这些目标值,并统计每个目标值的出现次数。
-
计算操作次数:
- 对于每个目标值,计算将其变为目标值所需的操作次数。
- 使用缓存来存储每个元素变为目标值所需的操作次数,以减少重复计算。
代码实现
#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;
}
关键步骤解释
-
预处理所有可能的目标值:
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) { 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; } }