leetcode 5756. 两个数组最小的异或值之和(状态压缩dp)|周末学习

224 阅读2分钟

本文已参与周末学习计划,点击链接查看详情:juejin.cn/post/696572… image.png

题目

给你两个整数数组 nums1 和 nums2 ,它们长度都为 n 。

两个数组的 异或值之和 为 (nums1[0] XOR nums2[0]) + (nums1[1] XOR nums2[1]) + ... + (nums1[n - 1] XOR nums2[n - 1]) (下标从 0 开始)。

比方说,[1,2,3] 和 [3,2,1] 的 异或值之和 等于 (1 XOR 3) + (2 XOR 2) + (3 XOR 1) = 2 + 0 + 2 = 4 。 请你将 nums2 中的元素重新排列,使得 异或值之和 最小 。

请你返回重新排列之后的 异或值之和 。

 

示例 1:

输入:nums1 = [1,2], nums2 = [2,3]
输出:2
解释:将 nums2 重新排列得到 [3,2] 。
异或值之和为 (1 XOR 3) + (2 XOR 2) = 2 + 0 = 2

示例 2:

输入:nums1 = [1,0,3], nums2 = [5,3,4]
输出:8
解释:将 nums2 重新排列得到 [5,4,3] 。
异或值之和为 (1 XOR 5) + (0 XOR 4) + (3 XOR 3) = 4 + 4 + 0 = 8

解题思路

假设n是数组的长度,这题可以看成nums1的前c个数字,在nums2中找出c个数字,一一对应,从而使得异或和最小,因此我们可以使用n位的二进制数即可表示nums2的不同取值状态,第j位为1代表该情况下,nums2[j]已经被选择了.

  • 例如对于nums1 = [1,2], nums2 = [2,3]
  • 01表示nums2[1]=3已经被选择用于与nums1的前一个数字组成异或和了,dp[01]就是代表这种情况的最小异或和
  • 10表示nums2[0]=2已经被选择用于与nums1的前一个数字组成异或和了 所以dp[11]应该从dp[01]或者dp[10]转移而来,并且需要加上新产生的异或值

状态转移

找出当前状态的上一步状态,进行转移

  1. 遍历nums2的每一个元素
  2. (1<<j)&i>0代表nums2[j]在当前状态下已经被选择,所以我们将这个选择取消(将这一位置零),就是上一步的其中一种状态dp[(1<<j)^i]
		for j := 0; j < n; j++ {
			if (1<<j)&i>0{
				val:=dp[(1<<j)^i]+(nums1[cnt(i)-1]^nums2[j])
				if val<dp[i]{
					dp[i]=val
				}
			}

		}

代码

func minimumXORSum(nums1 []int, nums2 []int) (res int){

	n:=len(nums1)
	cnt := func(cur int) (res int) {
        
		for k := 0; k < 31; k++ {
			res+=1&cur
            cur>>=1
		}
		return
	}

	mask:=1<<n
	dp := make([]int, mask)
	for i := 1; i < mask; i++{
		dp[i]=2e9
	}
	for i := 1; i < mask; i++ {
		for j := 0; j < n; j++ {
			if (1<<j)&i>0{
				val:=dp[(1<<j)^i]+(nums1[cnt(i)-1]^nums2[j])
				if val<dp[i]{
					dp[i]=val
				}
			}

		}
	}
	return dp[mask-1]
}