题目描述
给了两个数组nums1和nums2,长度都是n,问怎么排列可以让数组对应元素的异或值之和最小?
f1-二进制枚举+状态压缩 |
基本分析
1.有没有是啥贪心做法,因为看到相同元素异或可以为0?用二进制枚举可以实现这里,还要考虑其他元素,没有啥思路
2.这里涉及到两个元素的排列,枚举需要枚举哪个数组?因为数组是可以任意变动的,就假设1不动,排列2让2去和1一一计算异或和就行了
3.考虑怎么选nums2的顺序?定义f[mask]表示我们选了nums2的的元素状态为mask,且选了数组nums1的前count(mask)个元素情况下的最小异或值之和。这里我们mask只表示选了哪些,具体顺序不管,最小值由枚举最后一位推出来
4.考虑怎么转移?
- 枚举nums2的mask,记下某个mask有多少个1,记为c
- 枚举mask的每一位1,表示这一位1放到最后去和nums1[c]去取异或;f[mask]的最小值就是其中某个转移。
代码
class Solution:
def minimumXORSum(self, nums1: List[int], nums2: List[int]) -> int:
n = len(nums1)
nmask = 1<<n
f = [float('inf')] * nmask
f[0]=0
for mask in range(1, nmask):
# 求出这个mask对应了多少个1
c = bin(mask).count('1')
for i in range(n):
# 找到了一个1的位置,这个位置可以和nums1[c-1]去取异或
if mask & (1<<i) != 0:
f[mask] = min(f[mask], f[mask-(1<<i)] + (nums1[c-1]^nums2[i]))
return f[-1]
复杂度
时间:\(O(n \cdot 2^n)\)
空间:\(O(2^n)\)
总结
题目思路:(1)贪心?乍一看是不是有贪心的做法,但是在其它不等的时候没有啥好思路;(2)怎么遍历两个?题目给了俩数组,不知道怎么用mask表示俩,因为可以任意动,所以值用mask表示一个就行了;(3)怎么用mask表示nums2的选择顺序?不需要顺序,只需要用mask表示nums2的选择情况,只枚举最后一位进行转移就行
代码细节:(1)某位是不是1的判断:判断某一位是不是有1的时候,注意不要写成mask & (1<<i) == 1,因为可能等于(1,2,4,8等等);(2)云端优先级,^运算比加法要低,需要注意括号。