📅  最后修改于: 2023-12-03 14:49:35.738000             🧑  作者: Mango
在给定一个长度为 n 的数组中,找到一种操作方法,使得每一次操作可以选择任意两个元素,并将它们的值都减少为它们的按位与运算值。重复进行这样的操作,直到所有的元素都相等,操作的总次数就是最小值。
我们可以考虑将题目中给定的数组按照二进制位的值进行分类,对于第 k 位为 1 的所有数,我们将其分为一个集合 S_k。由于两个数按位与的结果取决于位值均为 1 的位,因此如果一个数 x 和另外一个数 y 的第 k 位均为 1,那么只能操作这两个数,将它们的第 k 位变为 0。因此,只有在同一个集合中的数才有可能进行操作。
对于一个集合 S_k,如果其包含的数的数量小于 2,那么它无法进行任何操作,我们可以直接忽略它。否则,我们需要将其全部相加,从而得到 S_k 的总和 S_k_sum。注意到按位与的结果中 k 位均为 0,因此每次操作都会将 S_k 中的数的和减少 k 位的值。因此,集合 S_k 需要进行的最小操作数为 k * (|S_k| - 1),其中 || 表示集合的大小。
最终的答案就是所有集合需要进行的最小操作数之和。
下面是 Python 代码实现:
from typing import List
def min_bitwise_and(nums: List[int]) -> int:
n = len(nums)
max_bit = max(nums).bit_length() - 1
cnt = [0] * (max_bit + 1)
for num in nums:
for i in range(max_bit + 1):
if num & (1 << i):
cnt[i] += 1
ans = 0
for i in range(max_bit + 1):
if cnt[i] >= 2:
ans += (1 << i) * (cnt[i] - 1)
return ans
本算法的时间复杂度为 O(nlogm),其中 m 为数组中最大值的二进制位数。空间复杂度为 O(logm)。由于需要遍历数组并统计每个数字的每个二进制位的值,因此时间复杂度的下限为 O(nlogm)。
本题给出的算法思路比较巧妙,对于每个集合 S_k,都需要仅需要操作集合中的任意两个数。这其实是利用了按位与运算的性质。本题还可以有另外一种做法,即对于每个位置 i,统计数组中所有元素二进制表示中第 i 位为 1 的个数 p_i,按照 p_i 乘以 2 的 i 次方进行排序,从小到大进行遍历,并对相邻两个集合进行合并,直到最后只剩下一个集合,此时的操作数即为答案。这种做法的时间复杂度也为 O(nlogm)。