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 };
View Code

 

其次是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 };
View Code

 

然后是官方正解,也就是状态压缩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 };
View Code

 

posted @ 2021-06-02 21:08  hk_lin  阅读(148)  评论(0编辑  收藏  举报