leetcode 1879.两个数组最小的异或值之和 模拟退火 KM 状压dp
给你两个整数数组 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.length
n == nums2.length
1 <= n <= 14
0 <= nums1[i], nums2[i] <= 107
首先是模拟退火,每次交换nums2中的两个位置的值进行模拟退火,最终将退火中计算得出的最小值作为答案。
1 class Solution { 2 public: 3 int minimumXORSum(vector<int>& nums1, vector<int>& nums2) { 4 int n = nums2.size(); 5 random_shuffle(nums2.begin(),nums2.end()); 6 int t = 0,dt = 0; 7 for(int i=0;i<n;i++) 8 t += nums1[i] ^ nums2[i]; 9 int ans = t; 10 for(double T = 1e6;T > 1e-18;T *= 0.999){ 11 int x = rand() % n,y = rand() % n; 12 dt = - (nums1[x] ^ nums2[x]) - (nums1[y] ^ nums2[y]) + (nums1[x] ^ nums2[y]) + (nums1[y] ^ nums2[x]); 13 ans = min(ans,t + dt); 14 if(dt < 0){ 15 t += dt; 16 swap(nums2[x],nums2[y]); 17 }else if(exp(-1.0 * dt / T) * RAND_MAX > rand()){ 18 t += dt; 19 swap(nums2[x],nums2[y]); 20 } 21 } 22 return ans; 23 } 24 };
其次是KM算法求二分图的最小权匹配,将nums1和nums2中的每个元素之间都建边,边值为两者异或结果的相反数,然后跑KM算法求二分图的最大权匹配即可。
1 class Solution { 2 public: 3 vector<vector<int> > wei; 4 int n; 5 vector<int> visx, visy, linky, lx, ly; 6 int xnum, ynum; 7 int findPath(int now){ 8 visx[now] = 1; 9 for(int i=0;i<ynum;i++){ 10 if(!visy[i] && lx[now] + ly[i] == wei[now][i]){ 11 visy[i] = 1; 12 if(linky[i] == -1 || findPath(linky[i])){ 13 linky[i] = now; 14 return 1; 15 } 16 } 17 } 18 return 0; 19 } 20 int km(){ 21 linky = vector<int> (ynum,-1); 22 ly = vector<int> (ynum,0); 23 lx = vector<int> (xnum,-0x3f3f3f3f); 24 for(int i=0;i<xnum;i++){ 25 for(int j=0;j<ynum;j++){ 26 lx[i] = max(lx[i],wei[i][j]); 27 } 28 } 29 for(int i=0;i<xnum;i++){ 30 while(1){ 31 visx = vector<int> (xnum,0); 32 visy = vector<int> (ynum,0); 33 if(findPath(i)) break; 34 int d = 0x3f3f3f3f ; 35 for(int j=0;j<xnum;j++) 36 if(visx[j]) 37 for(int k=0;k<ynum;k++) 38 if(!visy[k]) 39 d = min(d,lx[j] + ly[k] - wei[j][k]); 40 if(d == 0x3f3f3f3f) return -1; 41 for(int j=0;j<xnum;j++) if(visx[j]) lx[j] -= d; 42 for(int j=0;j<ynum;j++) if(visy[j]) ly[j] += d; 43 } 44 } 45 int ans = 0; 46 for(int i=0;i<ynum;i++){ 47 if(linky[i] > -1) ans += -wei[linky[i]][i]; 48 } 49 return ans; 50 } 51 int minimumXORSum(vector<int>& nums1, vector<int>& nums2) { 52 n = nums1.size(); 53 xnum = n,ynum = n; 54 wei = vector<vector<int> > (n,vector<int> (n,0)); 55 for(int i=0;i<n;i++){ 56 for(int j=0;j<n;j++){ 57 wei[i][j] = - (nums1[i] ^ nums2[j]); 58 } 59 } 60 return km(); 61 } 62 };
然后是官方正解,也就是状态压缩dp,官方解如下
1 class Solution { 2 public: 3 int minimumXORSum(vector<int>& nums1, vector<int>& nums2) { 4 int n = nums1.size(),m = 1 << n; 5 vector<int> f(m,INT_MAX); 6 f[0] = 0; 7 for(int mask=1;mask<m;mask++){ 8 for(int i=0;i<n;i++){ 9 if(mask & (1 << i)){ 10 f[mask] = min(f[mask],f[mask ^ (1 << i)] + (nums1[ __builtin_popcount(mask) - 1] ^ nums2[i])); 11 } 12 } 13 } 14 return f[m - 1]; 15 } 16 };