字典树,dfs
题目意思:
有n个数, 求一个数x ,使得 max{a[i] ^ x}(1 <= i <= n) 最小。
0 ^ 0 == 0, 1 ^ 1 == 0, 0 ^ 1 == 1, 也是是相同的 异或为0, 不同的异或为1;
本题要点:
1、字典树处理 数的异或问题:
用这 n 个数建立一颗 trie, 注意到 a[i] <= 2^30 - 1, 因此每一个数都可以看做是 30 位 的 01 串。
怎样寻找到 x 呢。 一般的查询,是从树的根部开始扫描, 到了某个节点 fa,
如果fa 只有一个孩子 left,这条查找路径只能往 left 方向走了。
如果fa 同时有两个孩子,这时候就要判断 从 left 走还是从 right 走。(假设这个点 fa 对应的是第k位)
此时,x 无论从 left 走,还是从 right 走,都要 加上 第 k 位对应的数值(1 << k), 也就是 2^k
2、 用数组 val[k] 表示 ,trie 树对应的节点k, 这个节点k走到叶子节点,至少产生的数值。
设计 dfs 函数: void dfs(int root, int depth),
root 表示当前的节点编号
depth 表示当前节点,对应的第k位。 trie 根节点对应的是 29, 叶子节点对应的是0.
最后 val[0] 就表示 根节点 0 至少产生的数值,就是答案。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MaxN = 1e5 + 10;
int a[MaxN];
int n;
int trie[32 * MaxN][2];
int idx = 0;
int val[32 * MaxN];
void insert(int x)
{
int p = 0;
for(int i = 29; ~i; --i)
{
int& s = trie[p][x >> i & 1];
if(!s)
s = ++idx;
p = s;
}
}
void dfs(int root, int depth)
{
if(depth < 0)
return;
int L = trie[root][0], R = trie[root][1];
if(0 == L && 0 == R) // 叶子节点
return;
if(0 == L) // 左子树为空, 往右走
{
dfs(R, depth - 1);
val[root] = val[R];
}else if(0 == R){
dfs(L, depth - 1);
val[root] = val[L];
}else{
dfs(L, depth - 1);
dfs(R, depth - 1);
if(val[L] < val[R])
{
val[root] = val[L] + (1 << depth);
}else{
val[root] = val[R] + (1 << depth);
}
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
insert(a[i]);
}
dfs(0, 29);
printf("%d\n", val[0]);
return 0;
}
/*
3
1 2 3
*/
/*
2
*/
/*
2
1 5
*/
/*
4
*/