最大异或对
传送描点:活动 - AcWing
在给定的 N 个整数 A1,A2……AN 中选出两个进行 xor(异或)运算,得到的结果最大是多少?
输入格式
第一行输入一个整数 N。
第二行输入 N 个整数 A1~AN。
输出格式
输出一个整数表示答案。
数据范围
1≤N≤105,
0≤Ai<231
输入样例:
3
1 2 3
输出样例:
3
思路
这题一开始的话,会想着通过二重循环,求出最大值,但这肯定超时
优化:题目是两个数异或最大值(即ai、aj),假如ai确定,我们现在就是要求aj使ai异或aj最大,,那怎样异或最大呢?
假设确定ai=11001100101011...11001,共31位,从最高位开始(从左往右),看看与ai当前位相反的数是否存在,eg.ai的第31位为1,那么我就要看第aj的第31位是否为0,若存在,就走0分支,继续看第30位是否存在0的分支……若不存在,那么第31就走1的分支,再看第30位是否存在0的分支……类似于贪心
son[M][2]
其中M表示节点数的上限,2表示每个节点的孩子节点数量,son[p][0]
表示节点p的左孩子节点,son[p][1]
表示节点p的右孩子节点。
那有问题来了,节点数应该要多大才合适?(答案是N*31,原因见下述解释)
AcWing 143. 最大异或对 —— 神奇的二进制 - AcWing
还要熟悉位运算操作,x>>k&1
求出第k位0、1的情况(最低位(即最右边)是最低位)
code
#include <iostream>
using namespace std;
const int N = 1e5+5, M = 31*N;
int nums[N],son[M][2],id;// son[M][2]代表节点值编号,M为节点值,id代表节点数
void insert(int x)
{
int pos = 0;
for(int i=30;i>=0;i--)//由题目知Ai小于2的31次方
{
int num = x>>i&1;//第i位0、1情况
if(!son[pos][num])
son[pos][num] = ++id;
pos = son[pos][num];
}
}
int query(int x)
{
int res = 0,pos = 0;
for(int i=30;i>=0;i--)
{
// 从高位向低位操作
// 对于当前位的二进制数
// 尽可能往其出现过的相反的方向走,假设当前位 u = 0
// 若1的那个方向的 trie树枝干被创建则向那个方向走
// 若没有被创建,则先将就一下走0的方向
int num = x>>i&1;//第i位0、1情况
if(son[pos][!num]){//!num代表即!0 == 1,!1 == 0
pos = son[pos][!num];
res = (res<<1)+!num;
}
else{
pos = son[pos][num];
res = (res<<1)+num;
}
}
return res;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>nums[i];
int res = 0;//异或最大值
for(int i=0;i<n;i++){
//先插入后运算是为了避免边界问题,对于第一个整数整个树为空
//若先将自己插入进去,则与自己异或结果为始终为0
insert(nums[i]);
int t = query(nums[i]);
// 看是否是所有整数中的最大异或对
res = max(res,nums[i]^t);
}
cout<<res<<endl;
}
参考文献:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!