Acwing 143. 最大异或对(字典树+贪心)

地址:https://www.acwing.com/problem/content/145/

没买课的话,应该打不开题。我就截个图吧,题意是很简单的。

解析:

关于位运算,我有个小笔记可供参考:https://www.cnblogs.com/liyexin/p/13914911.html

1:贪心思路

首先明确,什么是异或运算,两个数不同,则为1,否则为0。

给定ai,如果想让ai和一个数的异或结果最大,很明显,要从高位开始看,尽量从高位筛选同位不同数的数字。

举个例子:

10:1010

x

从高位(第3位)开始看,10的第三位为1,最优解是x的第三位与它不同,为0。10的第二位为0,最优解是x的第二位为1......以此类推。

这就是贪心的思路。

2:字典序优化

怎么优化呢?暴力跑的话是n*n。

发现每个数字化为二进制的话,按题中范围最大为31位。直接跑字典序的话,复杂度可以优化到n*31

如图所示:

总结一下,对于当前的二进制位,如果存在与它不同的,就走过去,否则,只能沿着仅存的那条路走了。

由于N是1e5,假设每个数都有31位而且都占有新节点,那么最多一共需要N*31个节点位,即要开到son[N*31][31]

代码:

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
const int maxn=1e5+10,maxn2=31*maxn;
int idx=0;
typedef long long ll;
int a[maxn];
int son[maxn2][2];
void insert(int x)
{
    int p=0;
    for(int i=30;i>=0;i--)
    {
        int u = x>>i&1;
        if(!son[p][u])
            son[p][u]=++idx;
        p=son[p][u];
    }
    return ;
}
int query(int x)
{
    int res=0;
    int p=0;
    for(int i=30;i>=0;i--)
    {
        int u=x>>i&1;
        if(son[p][!u])
        {
            res=res*2+1;
            p=son[p][!u];
        }            
        else
        {
            p=son[p][u];
            res=res*2;
        } 
    }
    return res;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        insert(a[i]);
    }
    int maxx=0;
    for(int i=1;i<=n;i++)
    {
        maxx=max(maxx,query(a[i]));
    }
    cout<<maxx<<endl;
    return 0 ; 
}

 

posted @ 2020-11-02 17:30  liyexin  阅读(100)  评论(0)    收藏  举报