1. 程式人生 > 實用技巧 >Acwing 143. 最大異或對(字典樹+貪心)

Acwing 143. 最大異或對(字典樹+貪心)

地址:https://www.acwing.com/problem/content/145/

沒買課的話,應該打不開題。我就截個圖吧,題意是很簡單的。

解析:

關於位運算,我有個小筆記可供參考:https://www.cnblogs.com/liyexin/p/13914911.html

1:貪心思路

首先明確,什麼是異或運算,兩個數不同,則為1,否則為0。

給定ai,如果想讓ai和一個數的異或結果最大,很明顯,要從高位開始看,儘量從高位篩選同位不同數的數字。

舉個例子:

10:1010

x

從高位(第3位)開始看,10的第三位為1,最優解是x的第三位與它不同,為0。10的第二位為0,最優解是x的第二位為1......以此類推。

這就是貪心的思路。

2:字典序優化

怎麼優化呢?暴力跑的話是n*n。

發現每個數字化為二進位制的話,按題中範圍最大為31位。直接跑字典序的話,複雜度可以優化到n*31

如圖所示:

總結一下,對於當前的二進位制位,如果存在與它不同的,就走過去,否則,只能沿著僅存的那條路走了。

由於N是1e5,假設每個數都有31位而且都佔有新節點,那麼最多一共需要N*31個節點位,即要開到son[N*31][31]

程式碼:

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include
<iostream> #include<vector> using namespace std; const int maxn=1e5+10,maxn2=31*maxn; int idx=0; typedef long long ll; int a[maxn]; int son[maxn2][2]; void insert(int x) { int p=0; for(int i=30;i>=0;i--) { int u = x>>i&1; if(!son[p][u]) son[p][u]
=++idx; p=son[p][u]; } return ; } int query(int x) { int res=0; int p=0; for(int i=30;i>=0;i--) { int u=x>>i&1; if(son[p][!u]) { res=res*2+1; p=son[p][!u]; } else { p=son[p][u]; res=res*2; } } return res; } int main() { int n; cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; insert(a[i]); } int maxx=0; for(int i=1;i<=n;i++) { maxx=max(maxx,query(a[i])); } cout<<maxx<<endl; return 0 ; }