开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第34天,点击查看活动详情
题目描述
在给定的 N 个整数 A1,A2……AN中选出两个进行 xor(异或)运算,得到的结果最大是多少?
输入格式
第一行输入一个整数 N。
第二行输入 N 个整数 A1~AN。
输出格式
输出一个整数表示答案。
数据范围
,
输入样例:
3
1 2 3
输出样例:
3
想想最朴素的做法:暴力循环
最简单的做法就是每一个数和其他的数进行一一比较,然后会有一个异或对的结果
想想看,我们要怎么完成这个循环?难道是最麻烦的从左到右吗?不是,对比的原则是
- 每插入一个数,我们将这个数和之前的全部数字进行比较即可,不需要真的将所有的数和其他数字比较一般,这样太麻烦了,也太耗时了。
- 分析一下时间复杂度吧,按照这样的做法的话,时间复杂度的计算是一个等差数列的求和计算方法,好的看看n是多少——,我的评价是6,时间复杂度差不多是,C++是快,但是这样子做绝对会爆时间的,所以我们需要有其他的算法来完成
解决方法:Tire树存储
我们将一个数抽象成为一个Tire树,比如样例:1 2 3
1的二进制数是1
2的二进制数是10
3的二进制数是11
那么他们的Tire树大致的结构就是:
所以这就是一个简单的Tire树结构,那么怎么算最大的异或对呢?
计算异或对
首先看看样例:1 2 3
1和2比->01和10,结果就是11->3
1和3比->01和11,结果就是10->2
2和3比->10和11,结果技术01->1
我们会发现,二进制从前往后比较如果越前的位数越不同(01)的话,异或对就越大,所以我们参照之前等差数列的那种异或求法,每个数插入之后,先存储进Tire数,然后比较二进制从前往后的数值,尽量往数字越不同的地方前进,这样的好处就是只需要在若大的数字里面只要在Tire数里面走一次
我将会在下面的代码加上足够的代码解释:
Tire树代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
////M代表一个数字串二进制可以到多长
const int N = 100010, M = 3100010;
int n;
int a[N], son[M][2], idx;
void insert(int x) {
//根节点
int p = 0;
for (int i = 30; i >= 0; i--) {
int u = x >> i & 1;//先左移i位,就可以取出第i位的数字,然后再异或一下是1还是0
//如果没找到这条路,开辟一条新路
if (!son[p][u])son[p][u] = ++idx;
//指向下一层
p = son[p][u];
}
}
int search(int x) {
int p = 0, res = 0;
//从最大位开始比较
for (int i = 30; i >= 0; i--) {
int u = x >> i & 1;
//根据异或的最大值,我们要找不同的,如果有不同的就走另一条条路,如果没有那只好走u这条路
if (son[p][!u]) {
p = son[p][!u];
res = res * 2 + 1;//相当于左移一位
}
else {
p = son[p][u];
res = res * 2;
}
}
return res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
insert(a[i]);
}
int res = 0;
for (int i = 0; i < n; i++)res = max(res, search(a[i]));
cout << res;
return 0;
}
解题结束~