题目链接:CSU_1216
题目简述:
经典题目,求一个数组中两个数异或运算的最大值。题目极其简单,但是要求的复杂度需要达到O(N * log(N)),还是比较难的。
解题思路:
总的思路就是构建一棵0-1字典树,然后一个数让查找一个与其异或结果最大的数的效率达到O(log(N)),这里因为异或的特殊性质,可以使用贪心法则来实现。
1、0-1字典树:
这里其实是就是二叉树,之所以叫做字典树是因为我们的算法把一个数当成了一个31位的字符串来看,比如1就是三十个零外加一个一,这一部分还是比较简单的。
2、贪心找最大异或值:
异或运算有一个性质,就是对应位不一样为1,我们要让结果最大化,就要让越高的位置为1。我们找跟一个数的异或结果最大的数,就从树的根结点(最高位)开始找,如果对应位置这个数是0,优先去找那一位为1的数,找不到才去找0;如果对应位置这个数是1,优先去找那一位为0的数,找不到才去找0;最终找到的数就是跟这个数异或结果最大的数。n个数,每个数找一个这样的数并算出结果求其中的最大值,可以得到答案。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,a[100005],tmp[35];
struct node{
int val;
node *left, *right;
node(int value=-1){
val = value;
left = right = NULL;
}
};
node *head = NULL;
void cal(int num){
memset(tmp,0,sizeof(tmp));
int cnt=0;
while(num){
tmp[cnt++]=num%2;
num/=2;
}
for(int i=0;i<15;i++)
swap(tmp[i],tmp[30-i]);
}
void insert(node* &head, int index, int &num){
if(index+1==31){
head->val = num;
return;
}
if(tmp[index+1]==0 && !head->left)
head->left = new node(-1);
if(tmp[index+1]==1 && !head->right)
head->right = new node(-1);
if(tmp[index+1]==0)
insert(head->left,index+1,num);
if(tmp[index+1]==1)
insert(head->right,index+1,num);
}
int dfs(node* &head,int index){
if(index+1==31)
return head->val;
if(tmp[index+1]==0){
if(head->right){
return dfs(head->right,index+1);
}else{
return dfs(head->left,index+1);
}
}else{
if(head->left){
return dfs(head->left,index+1);
}else{
return dfs(head->right,index+1);
}
}
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
head = new node(-1);
for(int i=0;i<n;i++)
{
cal(a[i]);
insert(head,-1,a[i]);
}
int ans=0;
for(int i=0;i<n;i++)
{
cal(a[i]);
ans = max(ans, a[i]^dfs(head,-1));
}
printf("%d\n",ans);
}
return 0;
}
总结:
简单的数据结构用得好可以做很多很奇妙的事情,贪心不能忘啊!