最大异或对
题目传送: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 }
追求吾之所爱
浙公网安备 33010602011771号