AcWing143 最大异或对(Trie树、异或)

题目:AcWing143 最大异或对

题解目录

  • 前言
  • 一、题目陈述
  • 二、解决思路
    • 1.暴力解决
    • 2.数字变为二进制存储统计的思考
  • 三、代码实现
  • 总结


前言

异或运算,相当于二进制数的不进位加法:两位相同结果为0,两位不同结果为1。这道题目求一些整数中的两个数,这两个数的异或值为这些数两两异或结果的最大值。解决方法的数据结构用到Trie树,算法主要是Trie树的构建,以及针对Trie树的查询。

一、题目陈述

AcWing143 最大异或对(Trie树、异或)_第1张图片

二、解决思路

1.暴力解决

可以通过两重循环来解决,复杂度显然是O(N*N),而C++ 1秒能大概算 1 0 7 10^7 107~ 1 0 8 10^8 108,相对于题目的规模显然不能使用。

2.数字变为二进制存储统计的思考

我们想要求一个数a的匹配数b,这个数ba的异或结果最大,自然是希望从最高位开始,数b的每一位都和数a相异。自然想到,可以用Trie树来存储每一个数的二进制表示,然后从高位查询异或和最大的数。即用一个Tire树来存下N个数的二进制形式的01串,串的长度是30,因为每个数最多不超过2^31所以,取30。然后根据异或的特性,枚举一下所有的数,取这个数每一位的反码,去树中寻找尽可能和反码向同的路径,并且记录下路径过程的异或结果即可。复杂度是O(N*logN)

三、代码实现

#include
#include
using namespace std;
const int N = 100010 , M=31*N;
int n;
int a[N];
// Trie树的数据结构 
int trie[M][2],idx;
void insert(int x) {
	/*
		将x的二进制表示插入Trie树中 
	*/ 
	int p=0;
	for(int i=30;i>=0;i--) {
		// 取出x的第i位数
		int u=x>>i&1; 
		if(!trie[p][u]) trie[p][u] = ++idx;
		p = trie[p][u]; 
	}
}
int query(int x) {
	/*
		返回与目前Trie树中,与x异或和最大的数 
	*/
	int p=0;
	// res是与x异或和最大的数 
	int res=0;
	for(int i=30;i>=0;i--) {
		// 取出x的第i位 
		int u=x>>i&1;
		if(trie[p][!u]) {
			p=trie[p][!u];
			res = res*2+!u;
		}
		else {
			p=trie[p][u];
			res=res*2+u;
		}
	}
	return res;
}
int main() {
	cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	int res = 0;
	for(int i=0;i<n;i++) {
		// 先插入再查询 目的是解决边界问题,最开始还没有存储任何一个数
		insert(a[i]); 
		int t=query(a[i]);
		res = max(res,a[i]^t); 
	}
	cout<<res<<endl;
	return 0;
}

总结

这道题是2021寒假第一题,也是刷题笔记系列的第一篇。主要用的思想是将数的二进制表示使用Trie树存储,然后进行查询。

你可能感兴趣的:(算法)