最大异或对
题目传送:https://www.acwing.com/problem/content/description/145/
在给定的N个整数A1,A2……AN<?XML:NAMESPACE PREFIX = "[default] http://www.w3.org/1998/Math/MathML" NS = "http://www.w3.org/1998/Math/MathML" />A1,A2……AN中选出两个进行xor(异或)运算,得到的结果最大是多少?
输入格式
第一行输入一个整数N。
第二行输入N个整数A1A1~ANAN。
输出格式
输出一个整数表示答案。
数据范围
1≤N≤1051≤N≤105,
0≤Ai<2310≤Ai<231
输入样例:
3
1 2 3
输出样例:
3
暴力是最简单的解题办法。这题先拿暴力来写。
1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 const int N = 1e6 + 5; 5 int a[N]; 6 7 int main() { 8 int n; 9 cin >> n; 10 for(int i = 0; i < n; ++ i) 11 cin >> a[i]; 12 int res = 0; 13 for(int i = 0; i < n; ++ i) 14 for(int j = 0; j < n; ++ j) 15 if(i != j) 16 res = max(res, a[i]^a[j]); 17 cout << res << endl; 18 return 0; 19 }
这种方法往往是,会带来完美的TLE。当然优化的方法就要通过观察,可以通过什么数据结构将复杂度降下来。
先观察暴力做法,循环的第一次是没法优化的,观察第二层循环,重复多次求最大值,这显然不是最优的。
先来了解异或,当两个二进制对应位上的值都不相同时就异或值就会最大。
这样看来我们只需要只找它与枚举的数,的二进制尽可能相反。在众多的数据结构中,字典树可以满足这个要求。
我们把每位数都当成32位二进制数,不够的补0.然后通过策略,每次都取对应位相反的数,否子取相同的。
1 #include <iostream> 2 #include <algorithm> 3 #include <string> 4 using namespace std; 5 const int N = 1e5 + 5, M = 31 * N; 6 7 int n; 8 int a[N]; 9 int trie[M][2], tot; 10 11 void insert(int x) { // 通过每个数的二进制来构建字典树,每个节点就只有两个指针 12 int p = 0; 13 for (int i = 31; i >= 0; -- i) 14 { 15 int u = x >> i&1;//取对应的位置的数 16 if(!trie[p][u]) trie[p][u] = ++ tot; 17 p = trie[p][u]; 18 } 19 } 20 21 int query(int x) { 22 int p = 0, res = 0; 23 for(int i = 31; i >= 0; -- i) {//每个数都当成二进制位,存储 24 int u = x >> i & 1; 25 if(trie[p][!u])//最优策略是走相反的路。 26 { 27 p = trie[p][!u]; 28 res = res*2 + 1;//对应位置相反,则res*2 + 1就相当于0-1 -> 1 ,*2相当于向左移动一位。 29 } 30 else { 31 p = trie[p][u];// 32 res = res * 2 + 0; 33 } 34 } 35 return res; 36 } 37 38 int main() { 39 cin >> n; 40 for(int i = 0; i < n; ++ i) 41 { 42 cin >> a[i]; 43 insert(a[i]); 44 } 45 46 int res = 0; 47 for(int i = 0; i < n; ++ i) { 48 res = max(res, query(a[i])); 49 } 50 cout << res << endl; 51 return 0; 52 }
追求吾之所爱