【Trie树】| AcWing 143. 最大异或对

题目描述

在给定的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

  1. ①与Ax和Trie树中A1到Ax-1之间的数匹配; ② Ax加入Trie树 。①②这两个的顺序可以没有先后要求,因为就算先把Ax插入到Trie树,自身与自身匹配结果为0,也没什么影响,不过还是建议先匹配再插入。
  2. 正常的顺序应该是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;

<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值