
题意分析
n个数字,从中选2个,使得这二者按位异或的结果最大,求最大值。
求解思路
异或的操作,不同为1,相同为0。
对于数x的二进制,当某一位为0时,查找是否有当前为为1的,如果有即选择当前为为1的这些数。
可以将n个数建立一个trie数,对于数x的每一位,x的当前位为0,则看当前节点是否有1的子节点,如果有则走向值为1的子节点,没有就继续向下走。
细化来看
目标 :A 异或 B 结果最大
贪心原则 :二进制数的高位权重远大于低位。为了让结果最大,我们要尽可能让高位变成 1。→ 从高位开始
我们希望找到一个数B ,使得 B的当前位是1-b (即与 b当前位 不同)。
对于 Trie 中当前位b
- 存在走向 1−b 的子节点 ,走这条路(能让当前位异或结果为 1,贡献最大值)。
- 如果不存在只能走 b 这条路(当前位异或结果为 0,虽然不完美,但为了继续匹配低位,只能妥协)。
代码实现
这是我的初代码
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],trie[N][2],cnt=1,maxn=0;
void insert(int x)
{
int p=1;
for(int i=31;i>=0;i--)
{
int b=(x>>i)&1;
if(!trie[p][b])
trie[p][b]=++cnt;
p=trie[p][b];
}
}
int query(int x)
{
int p=1;
int res=0;
for(int i=31;i>=0;i--)
{
int b=(x>>i)&1;
if(trie[p][!b]) //走相反的方向,有数
{
res=res| (1<<i);//这一位的结果是1
p=trie[p][!b];
}
else p=trie[p][b];
}
return res;
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
insert(a[i]);
}
for(int i=1;i<=n;i++)
{
maxn=max(maxn,query(a[i]));
}
cout<<maxn;
return 0;
}
只有80分,re了!
原因每个数最多贡献 31 个节点,总节点数约为N×31 。原数组太小导致越界 RE。
正确代码
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=(1e5+10)*31;
int a[N],trie[N][2],cnt=1,maxn=0;
void insert(int x)
{
int p=1;
for(int i=31;i>=0;i--)
{
int b=(x>>i)&1;
if(!trie[p][b])
trie[p][b]=++cnt;
p=trie[p][b];
}
}
int query(int x)
{
int p=1;
int res=0;
for(int i=31;i>=0;i--)
{
int b=(x>>i)&1;
if(trie[p][!b]) //走相反的方向,有数
{
res=res| (1<<i);//这一位的结果是1
p=trie[p][!b];
}
else p=trie[p][b];
}
return res;
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
insert(a[i]);
}
for(int i=1;i<=n;i++)
{
maxn=max(maxn,query(a[i]));
}
cout<<maxn;
return 0;
}