📌  相关文章
📜  使任何两个数组元素相等的最小按位与运算(1)

📅  最后修改于: 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)。