算法基础——最大异或对

原题连接ios

在给定的N个整数A1,A2……AN中选出两个进行xor(异或)运算,获得的结果最大是多少?函数

输入格式
第一行输入一个整数N。spa

第二行输入N个整数A1~AN。code

输出格式
输出一个整数表示答案。ci

数据范围
1 ≤ N ≤ 10^5,
0 ≤ Ai < 2^31get

输入样例:io

3
1 2 3

输出样例:stream

3

解题关键:循环

由于异或时只有当两个数相同二进制数位上的数字不一样时才会为 11 ^ 0 = 1, 0 ^ 1 = 1, 0 ^ 0 = 0, 1 ^ 1 = 0,因此要找出最大的异或对,就须要让两个数的二进制数位上的数尽量不一样便可。二进制

Q & A :

Q :这里对于 ~i 作一下解释,为何 ~i 能够等价于i >= 0
A :-1 在二进制中全是 1,因此 -1 的反码的二进制就全是 0 了,这样当 i-- 一直减,减到 -1 的时候就能跳出循环了。(🤪其实并无什么卵用,就只是在关键时候影响一下代码的可读性而已)。

最后,解释一下代码中的Query()函数中的 res += 1 << i;的意思。此处截取Y总的解释:若是当前子树中,存在第 i 位和 x 的第 i 位不一样的数,说明答案的第 i 位能够变成 1,就把 res 的第 i 位二进制位变成 1 。

完整AC代码:

#include <iostream>
#include <cstdio>

using namespace std;
const int N = 100010, M = 3100010;

int son[M][2], idx;

void Insert(int x){
    int p = 0;
    for(int i = 30; ~i; i--){   //~i等价于i >= 0,这里从x二进制的第31位开始枚举每一位上的数,因此这里左移了30位,枚举的是第31位上的数
        int u = x >> i & 1; //判断x这个数的第i位上的数是1仍是0
        if(!son[p][u]) son[p][u] = ++ idx;
        p = son[p][u];
    }
}

int Query(int x){
    int p = 0, res = 0;
    for(int i = 30; ~i; i--){
        int u = x >> i & 1; //若是u == 1说明第i为上的数为1,若是为0则反之。
        if(son[p][!u]){ //判断和x这个数的第i位上不同的数的这个分支是否是存在的。
            //若是存在,说明异或出来的这个数的第i位上的数能够为1
            res += 1 << i;
            p = son[p][!u];
        }
        //和x这个数的第i位上不同的数的这个分支不存在
        else p = son[p][u]; //不存在就只能让p走向存在的这个分支
    }
    return res;
}

int main(){
    int n, res = 0;
    cin >> n;

    //读入数据
    for(int i = 0; i < n; i++){
        int x;
        cin >> x;
        Insert(x);
        res = max(res, Query(x));
    }
    cout << res << endl;
    return 0;
}
相关文章
相关标签/搜索