[1597] Find MaxXorSum
时间限制: 2000 ms 内存限制: 65535 K
问题描述
Given n non-negative integers, you need to find two integers a and b that a xor b is maximum. xor is exclusive-or.
输入
Input starts with an integer T(T <= 10) denoting the number of tests.
For each test case, the first line contains an integer n(n <= 100000), the next line contains a1, a2, a3, ……, an(0 <= ai <= 1000000000);
输出
For each test case, print you answer.
样例输入
2
4
1 2 3 4
3
7 12 5
样例输出
7
11
题意:给出1e5个数,每个数小于等于1e9,求任意两个值使其异或值最大。
又是经典的给一堆数求某运算最大问题。也是经典的01字典树求异或和最大问题。听说曾作为今日头条的面试算法问题的开胃菜。
首先按照每个数的二进制建树,倒着从最高位开始建,因为不超过1e9的值,算每个数的二进制位31位,根节点是最高位,向下叶子节点为最低位。因为在求异或值时要尽量使得高位存在1。这也符合遍历数的顺序。
建树完成后就开始对每个数遍历,对1e5个数分别从根节点开始走,如果该数的当前位为1,那么查询树上同位是否有数存在0,也就是查询同一个父亲的另一个节点是否存在,如果存在则这一位置1,接着继续按照那个节点往下走,判断下一位和我们遍历的数相比是否存在相反的节点。
最后得到每一个数可以匹配到的最大异或值,输出即可。
对于查询的过程,如果对字典树有了解的话,都知道字典树是对单词的记录,查询时按照字符串匹配,不断往下走节点进行操作。 如此看来,01字典树在求异或时也是如此,我们记录了所有数字,现在对于一个要查询的数,求异或,那么就尽量从最高位开始使其在字典树中找到相同位置不同的节点,比如要给出的数是00001001010110,那么我们在字典树上就应该尽量找到11110110101001这样的遍历序列,如果某一位并不存在这样的节点,那只好有啥走啥,争取在后面的位中尽量取相反的匹配即可。
这里利用了字典树存储位数相同数节约空间的特点,同时也利用了字典树对大量数据方便查找的特点,既然无法对一个一个数进行n^2的一一比较,那么就把这些数存在字典树上。比较高位就比较了大量一样的数的高位。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
int num[maxn],n;
int tre[maxn<<4][2];
int tot;
void insert(int a,int rt)
{
for(int i=31;i>=0;i--)
{
int x=(a>>i)&1;
if(!tre[rt][x])tre[rt][x]=++tot;
rt=tre[rt][x];
}
}
int finds(int a,int rt)
{
int res=0;
for(int i=31;i>=0;i--)
{
int x=(a>>i)&1;
res<<=1;
if(tre[rt][!x])rt=tre[rt][!x],res++;
else rt=tre[rt][x];
}
return res;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
tot=0;
int rt=++tot;
memset(tre,0,sizeof tre);
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&num[i]),insert(num[i],rt);
int ans=0;
for(int i=0;i<n;i++)
ans=max(ans,finds(num[i],rt));
printf("%d
",ans);
}
return 0;
}