【力扣roadmap】2939. 最大异或乘积

15 阅读3分钟

题目描述

image.png

思路

这题,难倒是不难(手动抠鼻) 但需要想清楚边界,盯着代码好好想想就能调对。

最暴力的方法是,枚举[1,2^50]区间的每一个元素x,尝试更新(a ^ x) * ( b ^ x)的答案,包超时的。

你通过鼓捣题目给的三个例子,不难发现一个事实: 我们不妨假设aba \ge b ,那么二进制数a的长度a_len一定大于等于二进制数b的长度b_len的。 我们继续思考,如果现在不考虑n的大小,只是思考怎么对a和b进行异或,不难发现,两者异或相同的数x后会变得如何如何,其实只需要考虑a和b本身情况是什么样就好了,不必考虑x这个第三者。

啥叫不用考虑x了?解释一下,在a和b的某相同二进制位下,

  • a的该位为0,b的该位为0,那么一定要异或一个这个位为1的数x,才能同时让a和b变大,那么最终的乘积也会变大。

  • a的该位为1,b的该位为0,那么你可以异或一个数,让a的该位变成0,同时b的该位一定会变成1,就好像两者交换了该位的效果一样,这个效果还可以再次等价成a划出一部分大小给了b。这个结论不得了,因为你知道,为了让两个数的乘积变大,在两个数字的和不变的情况下,两个数字应该越接近他们的平均值,这个乘积就会越大

  • a的该位为0,b的该位为1,同理,这里不多赘述。

所以我们发现,在不考虑n的情况下,在相同位都为0,直接都变1;相同位都为1,不用动;相同位不同情况下,你看看能不能让交换该位后的新a和新b,距离平均值(交换该位前后平均值不变)更近?如果交换后的新a和新b距离平均值更新了,那么乘积一定会变大,用新a和新b直接替换掉旧a和旧b就好。

上面是不考虑n的情况下,默认假设我们可以操纵所有的位。现在我们引入n,情况会发生什么变化?

其实也没啥变化,就是需要考虑到n和a_len的关系。

  • 如果na_lenn \le a\_len, 那么仅考虑在n能操作的范围下对a和b进行变化。操作方法和上面一样。

  • 如果n>a_lenn \gt a\_len, 那么我们发现一个事实:就是同时操作a和b,将[a_len,n-1]的这些高位全部置成1,两者会变大,那么两者的乘积也会变大。

分析完毕,代码如下。

代码

class Solution:
    def maximumXorProduct(self, a: int, b: int, n: int) -> int:
        MOD = int(1e9) + 7 
        if a < b : a , b = b , a 
        a_len = 0
        tmp = a  
        while tmp > 0 :
            tmp >>= 1 
            a_len += 1 
        if a_len >= n :
            # 不涉及更高位
            for i in range(n-1,-1,-1) :
                avg = (a + b) / 2 
                tmp_a = a 
                tmp_b = b 
                if ((tmp_a >> i & 1) == 0 ) and ((tmp_b >> i & 1 ) == 0) : # 这一位都是0 需要全变
                    a |= 1 << i 
                    b |= 1 << i 
                    continue  # 直接变 不检查
                elif ( (tmp_a >> i & 1) == 1) and ((tmp_b >> i & 1) == 0):  # 有1有0
                    tmp_a -= 1 << i
                    tmp_b |= 1 << i 
                elif ( (tmp_a >> i & 1) == 0) and ((tmp_b >> i & 1) == 1): # 有1有0
                    tmp_b -= 1 << i
                    tmp_a |= 1 << i 
                else :
                    continue 
                if abs(a-avg) + abs(b-avg) > abs(tmp_a-avg) + abs(tmp_b-avg) :
                    a = tmp_a 
                    b = tmp_b
        else :
            # 先对a和b进行调整
            for i in range(a_len-1,-1,-1) :
                avg = (a + b) / 2 
                tmp_a = a 
                tmp_b = b 
                if ((tmp_a >> i & 1) == 0 ) and ((tmp_b >> i & 1 ) == 0) :
                    a |= 1 << i 
                    b |= 1 << i 
                    continue 
                elif ( (tmp_a >> i & 1) == 1) and ((tmp_b >> i & 1) == 0):
                    tmp_a -= 1 << i
                    tmp_b |= 1 << i 
                elif ( (tmp_a >> i & 1) == 0) and ((tmp_b >> i & 1) == 1):
                    tmp_b -= 1 << i
                    tmp_a |= 1 << i 
                else :
                    continue 
                if abs(a-avg) + abs(b-avg) > abs(tmp_a-avg) + abs(tmp_b-avg) :
                    a = tmp_a 
                    b = tmp_b
            for i in range(n-1,a_len-1,-1): # 将超出位以及最高位全置成1 
                a |= 1 << i 
                b |= 1 << i 
        return a * b % MOD