#P2778. 第2题-二进制数组
-
1000ms
Tried: 73
Accepted: 16
Difficulty: 4
所属公司 :
阿里
时间 :2025年3月30日-阿里云(开发岗)
-
算法标签>贪心算法
第2题-二进制数组
问题分析
小红有一个整数数组,要求在数组元素上执行最多两次操作(选择一个元素,使其加1),使得数组元素乘积的二进制末尾0的个数尽可能多。我们需要找出操作后的最优方案,返回二进制末尾0的最大数量。
二进制末尾0的概念
二进制末尾0的数量,实际上就是数字中包含因子2的个数。换句话说,数字中2的幂次方能分解出多少个2。
例如:
4的二进制是100,末尾有2个0,说明4 = 2^2。8的二进制是1000,末尾有3个0,说明8 = 2^3。
因此,要使得数组元素乘积的二进制末尾有更多的0,我们需要尽量让每个数包含更多的因子2。
题解思路
-
分析因子2的个数: 对每个数组元素,计算其包含的因子2的个数。通过将数不断除以2,直到不能再整除为止,统计次数。
-
操作的意义: 操作的目标是增加因子2的数量。通过将数组元素加1来增加因子2的数量。要注意,如果加1后,原来数中的因子2数量会发生变化,因此要分析每个数加1后的情况。
-
最大化末尾0的数量: 为了最大化末尾0的数量,我们首先记录下每个数当前的因子2个数,然后尝试对每个数进行加1操作,计算加1后因子2个数的变化,最终选择加1操作的最优位置。
-
具体步骤:
- 计算原始数组每个元素的因子2个数。
- 如果一个数加1后能获得更多的因子2,则进行加1操作。
- 尝试进行最多两次加1操作,最终计算操作后的最大末尾0数量。
复杂度分析
- 对于每个数,计算其因子2个数的时间复杂度是
O(log a_i),其中a_i是数组中的一个元素。对于n个元素,总时间复杂度是O(n log A),其中A是数组元素的最大值。 - 总体来说,算法时间复杂度为
O(n log A),适用于n最大为10^5,a_i最大为10^9的情况。
代码实现
Python实现
# 输入处理
n = int(input()) # 数组大小
a = list(map(int, input().split())) # 数组元素
# 计算一个数的因子2的个数
def count_factors_of_2(x):
count = 0
while x % 2 == 0:
count += 1
x //= 2
return count
# 记录每个数的因子2个数
factor_2_count = [count_factors_of_2(x) for x in a]
# 记录加1后的因子2个数变化
increase_factor_2 = []
# 对每个数分析加1后的因子2个数
for x in a:
new_x = x + 1
original_factors = count_factors_of_2(x)
new_factors = count_factors_of_2(new_x)
increase_factor_2.append(max(new_factors - original_factors,0))
# 排序增加因子2的效果,选择前两个增加最大的
increase_factor_2.sort(reverse=True)
# 计算原始因子2总数
total_factor_2 = sum(factor_2_count)
# 对增加的因子2进行最多两次操作
max_increase = sum(increase_factor_2[:2])
# 输出结果
print(total_factor_2 + max_increase)
Java实现
import java.util.*;
public class Main {
// 计算一个数的因子2的个数
public static int countFactorsOf2(int x) {
int count = 0;
while (x % 2 == 0) {
count++;
x /= 2;
}
return count;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // 数组大小
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt(); // 数组元素
}
// 记录每个数的因子2个数
int[] factor2Count = new int[n];
for (int i = 0; i < n; i++) {
factor2Count[i] = countFactorsOf2(a[i]);
}
// 记录加1后的因子2个数变化
List<Integer> increaseFactor2 = new ArrayList<>();
for (int i = 0; i < n; i++) {
int originalFactors = countFactorsOf2(a[i]);
int newFactors = countFactorsOf2(a[i] + 1);
increaseFactor2.add(Math.max(newFactors - originalFactors, 0));
}
// 排序增加因子2的效果,选择前两个增加最大的
Collections.sort(increaseFactor2, Collections.reverseOrder());
// 计算原始因子2总数
int totalFactor2 = 0;
for (int i = 0; i < n; i++) {
totalFactor2 += factor2Count[i];
}
// 对增加的因子2进行最多两次操作
int maxIncrease = 0;
for (int i = 0; i < Math.min(2, increaseFactor2.size()); i++) {
maxIncrease += increaseFactor2.get(i);
}
// 输出结果
System.out.println(totalFactor2 + maxIncrease);
}
}
C++实现
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 计算一个数的因子2的个数
int countFactorsOf2(int x) {
int count = 0;
while (x % 2 == 0) {
count++;
x /= 2;
}
return count;
}
int main() {
int n;
cin >> n; // 数组大小
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i]; // 数组元素
}
// 记录每个数的因子2个数
vector<int> factor2Count(n);
for (int i = 0; i < n; i++) {
factor2Count[i] = countFactorsOf2(a[i]);
}
// 记录加1后的因子2个数变化
vector<int> increaseFactor2;
for (int i = 0; i < n; i++) {
int originalFactors = countFactorsOf2(a[i]);
int newFactors = countFactorsOf2(a[i] + 1);
increaseFactor2.push_back(max(newFactors - originalFactors, 0));
}
// 排序增加因子2的效果,选择前两个增加最大的
sort(increaseFactor2.rbegin(), increaseFactor2.rend());
// 计算原始因子2总数
int totalFactor2 = 0;
for (int i = 0; i < n; i++) {
totalFactor2 += factor2Count[i];
}
// 对增加的因子2进行最多两次操作
int maxIncrease = 0;
for (int i = 0; i < min(2, (int)increaseFactor2.size()); i++) {
maxIncrease += increaseFactor2[i];
}
// 输出结果
cout << totalFactor2 + maxIncrease << endl;
return 0;
}
题目内容
小红拿到了一个数组,她可以进行最多两次操作:选择一个元素,使其加1。
小红希望操作结束后,数组所有元素乘积的二进制末尾有尽可能多的0。你能帮帮她吗?
输入描述
第一行输入一个正整数n,代表数组的大小。
第二行输入n个正整数ai,代表数组的元素。
1≤n≤105
1≤ai≤109
输出描述
输出一个整数,代表操作结束后,数组所有元素乘积的二进制末尾0的数量。
样例1
输入
5
1 2 3 4 5
输出
6
说明
操作两次后数组变为[2,2,4,4,5],数组乘积为320,
二进制表示为101000000,有6个0。