题目描述
在给定的N个整数A1,A2……AN中选出两个进行xor(异或)运算,得到的结果最大是多少?
输入格式
第一行输入一个整数N。
第二行输入N个整数A1~AN。
输出格式
输出一个整数表示答案。
数据范围
1≤N≤105,
0≤Ai<231
输入样例:
3
1 2 3
输出样例:
3
思路分析
0≤Ai<231,即最大值是231-1,即31个1,所以整数的二进制表示最大是31位,也就是说可以变成长度为31位的二进制字符串,所以进行异或运算的结果最大是1111……11 (31个1)
根据异或的性质,不同为1,相同为0,所以两位十进制数a和b,a^b
,当挑选的a和b的每个二进制位都互不相同时,最终会得到最大的异或结果(31个1)。
既然如此话,那么我们可以这么做,每一次检索的时候,我们都走与当前 ai 这一位相反的位置走,也就是让异或值最大,如果说没有路可以走的话,那么就走相同的路。
查找异或对的过程:
查找是相互的过程,a找到b等价于b找到a,所以与其找一个数可以找几个数匹配,倒不如让一个数可以被几个数匹配:
先把第一个数A1存入Trie树 (初始化) ,然后A2只能和Trie树中的A1匹配,匹配完后A2加入到Trie树,然后是A3,只能和Trie树中的A1或者A2匹配,找到结果最大的即可,然后是A4……依次类推,一直到AN 。
- ①与Ax和Trie树中A1到Ax-1之间的数匹配; ② Ax加入Trie树 。①②这两个的顺序可以没有先后要求,因为就算先把Ax插入到Trie树,自身与自身匹配结果为0,也没什么影响,不过还是建议先匹配再插入。
- 正常的顺序应该是Ax和Ax+1到AN之间的数,即匹配A1先和A2到AN之间的数匹配,A2和A3到AN之间的数匹配,但是这样不方便插入Trie树,所以采用逆向思维。
问题关键:
1. 二进制数存入Trie树
二进制数,只有0和1,所以分支只有两个。
每个十进制数唯一对应一个二进制数,因此Trie树中从第1层到第31层,只要一个分支不同,就是选择了截然不同的数。
为了得到的结果最大,应该先从最高位开始考虑,在最高位取得最优解的前提下,再去低位找。
e.g. 假如x=(10010)2 ,它的最大异或对应该是(01101)2,这样就是全1了。
如果trie树中有(01101)2的话,那么从高位开始匹配还是从低位开始匹配,匹配到的就是(01101)2;
但是如果trie树中没有的话,假如Trie树中最大的是(01000)2 , 第0位和第2位取相同,其它位取不同,从最高位开始匹配可以找到(01000)2 ,但是如果从最低位匹配的话,最低位的0肯定要去找Trie树中的1,这样无论如何都匹配不到(01000)2 了。
2. 查找过程
给一个数x,查找Trie树中它的最大异或对:
从最高位开始检索,每一次检索的时候,我们都走与当前 ai 这一位相反的位置走,也就是让异或值最大,如果说没有路可以走的话,那么就走相同的路。
3. 把一个整数拆成31位二进制表示
int x,t;
for (int i=30;i>-1;i--) t=x>>i&1;
从最高位开始取,每次取一位。
且这里只能取1或0,不能取别的数,所以不能写成t=x&(1<<i);
代码实现
分析231,
210和103近似,所以230大概和109近似,即1e9,231大概是2e9,小于3e9,可以用int型定义trie二维数组。
#include <iostream>
#define read(x) scanf("%d",&x)
using namespace std;
<