原题链接如下:
AcWing 143.最大异或对
大佬题解链接:题解
先给个大致图解
截图自y总的上课笔记
这题首先暴力做法就不阐述了,相信大家都会
然后就按照y总教我们的,暴力一般是两层循环,我们就要从暴力之中去寻找优化,第一层的枚举一般不优化,优化的一般都是第二层
第二层我们去找到一些性质可以让我们不需要枚举每一个数字从而浪费时间,我们一直都往更优的方向去寻找
首先我们想到,比如我们第一层首先枚举2,2的二进制是010,我们找和2的最大的异或对的话,我们都知道,从最高位开始,1肯定是比0要更加优秀的,比如1100
肯定是要比0100
更大的,所以我们就可以从2二进制的最高位开始,每次都尽可能得去找,现有数据之中有没有当前位是和2的当前位不一样的(不一样的话,异或结果就是1,也即满足了我们想让结果尽可能大的需求),如果有,那就更好,如果没有,我们就当前位被迫取1(将就一下),然后重复此过程,一直到2的二进制最低位结束,我们每次都尽可能去寻找和当前枚举数的二进制位不同的数字,这其实也是一种贪心的思想。然后回到代码实现,我们可以很自然地去想到,我们可以用Trie树去实现,把样例中的每个数字每个二进制位按照顺序存储到Trie树(该题目由于二进制位只有01两种情况,所以我们可以采用二叉树)当中(如上图所示),把所有数存储进去之后,我们针对每次枚举的数字,在Trie树种寻找当前情况下最优的和他匹配的数字(异或值最大),基本思路就是遍历Trie树,每次遍历都尽力寻找和枚举树二进制位不一样的数,如果有就res中二进制位加入一个1,如果找不到,那只能将就当前位是0,然后进行下一轮匹配,用文字描述可能会很晦涩难懂,建议直接从代码上去理解
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010,M = 3100010;
int n;
int a[N],son[M][2],idx;
void insert(int x)
{
//这里差不多就是默写一下trie树的模板
int p = 0;
//x>>i&1表示的是把x的二进制数中的第i位(从右往左数)取出来
//例如x为1101,则x>>2&1的返回的结果就是0
for(int i = 30;i>=0;i--)
{
int &s = son[p][x>>i&1];
if(!s) s = ++idx;
p = s;
}
}
int search(int x)
{
int p = 0,res = 0;
for(int i = 30;i>=0;i--)
{
int s = x>>i&1;
if(son[p][!s])
{
res += 1<<i;
p = son[p][!s];
}
else p = son[p][s];
}
return res;
}
int main()
{
cin>>n;
for(int i = 0;i<n;i++)
{
scanf("%d",&a[i]);
insert(a[i]);
}
int res = 0;
for(int i = 0;i<n;i++) res = max(res,search(a[i]));
cout<<res<<endl;
return 0;
}