题目描述
在给定的N个整数A1,A2……AN中选出两个进行xor(异或)运算,得到的结果最大是多少?
输入格式
第一行输入一个整数N。
第二行输入N个整数A1~AN。
输出格式
输出一个整数表示答案。
数据范围
1≤N≤105,
0≤Ai<231
输入样例:
3
1 2 3
输出样例:
3
【2021.2.20】最大异或对
思路:先从暴力出发,肯定是要两层for循环,铁定会超时,但是我们发现,对于一个数a[i]的二进制位,则如果要寻找一个最大的数与之异或值最大,则贪心地从最高位开始,寻找与其恰巧位相反的。
假设给定一个整型数的二进制位1001011,则期望与之异或最大的肯定是0110100
因此只要遍历一个元素a[i],从高位遍历其二进制位($j,j\in\{0,1\}$),每次寻找一个与$j$相反的位。如果不存在,那则保持当前的位。
如何搜索呢?可以借助trie树,因为trie树能够将每个含有相同前缀的字符串挂载到一起,因此,当遍历到第j位二进制位时,只需要从其中寻找与之相反的位即可,若不存在则按照原始位查询。
例子:
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
const int M = 32 * N; // 假设最多有N个整数,则最多有32*N个二进制位
int a[N];
int trie[M][2], cnt[M]; // 分别表示trie树,以及每个结点作为一个数字的末尾的次数
int idx = 1;
// 向trie树插入一个结点
void insert(int x) {
int p = 0; // 从根结点开始
for(int i = 31; i >= 0; i --) {
int k = x >> i & 1; // 获得整型数的二进制的第i位
if(!trie[p][k]) trie[p][k] = idx ++;
p = trie[p][k];
}
cnt[p] ++;
}
int main() {
int n, res = 0;
scanf("%d", &n);
for(int i = 0; i < n; i ++) scanf("%d", &a[i]);
// 建立trie树
for(int i = 0; i < n; i ++) insert(a[i]);
for(int i = 0; i < n; i ++) {
// 遍历当前的数a[i]的二进制:
int p = 0, r = 0;
for(int j = 31; j >= 0; j --) {
int k = a[i] >> j & 1;
// 每次试图从trie树中查找与当前位相反的数,假设当前数为10010,则当遍历第一个高位1时,
// 希望搜索是否存在0,如果存在,则与之异或的最大值一定在0对应的子树后面。如果没找到,则按照当前位向下搜索;
// 每次寻找到一个不相同位时,则累计异或结果
int t = (k + 1) % 2;
if(!trie[p][t]) p = trie[p][k]; // 如果没有找到与当前位不同的,则只能按照原始的位向下查找
else r += pow(2, j), p = trie[p][t];
}
res = max(res, r);
}
printf("%d", res);
return 0;
}