1. 程式人生 > >Find MaxXorSum 經典字典樹求異或最大值

Find MaxXorSum 經典字典樹求異或最大值

題目大意

給出n個數,求在這n個數中取兩個數使得他們異或最大

解題思路

非常經典利用字典樹求異或的最大值。
讀懂這個題目之後,對於初學者我們可能會思考下面的問題:
1. 兩層迴圈o(n^2)肯定能求出了,但是肯定會超時
2. 這道題怎麼做呢?
3. 思考一陣(在不知道用什麼演算法的情況下),會不會用把每個數轉換為二進位制,然後按位進行比對
4. 就算轉換為二進位制之後,怎麼進行按個比對呢?

上面的幾個問題就是我,看到這個題目的時候思考的幾個問題,我們知道字典是擁有公共字首的,對應於一個十進位制數的二進位制來講,每個二進位制數能轉換到字典樹裡面。
因為題目中對資料的要求不超過10^9那麼轉換為二進位制也就是不會超過2^32這個數,那麼我們對每一個數的儲存時通過把這個數變為32位數比如1儲存的就是0(31)1。建立之後我們在通過一次迴圈找這個數的二進位制的一根分支上有多少不同的,然後取最大的。不理解沒有關係,我們用題目中的第一個樣例來舉例:
樣例是
4
1 2 3 4
那麼我們先要把1這個轉換為字典中的節點,一位要轉換為32位的,所以前面的三十位都應該是0,最後一位是1那麼轉換為圖就是下面這個圖:
建立第一個節點

,然後我們就要在這個字典樹中建立第二個節點也就是2,2對應的32位二進位制位0(30)10,對應如下圖
建立第二個節點
一次的建立第三個節點和第四個節點,第三個節點如下:
第三個節點
第四個節點:
第四個節點
把整個字典樹完全做好之後就變為下面的圖:
總圖

由圖我們可以知道這棵樹一共有有32層,每一個節點最多擁有兩個節點分別是0和1。那麼我們怎麼用程式碼把這個字典樹建立起來呢?
因為一個節點最多有兩個節點,所以我們就用一個二維陣列son[MAXN][2]來儲存每個節點的值,son[i][alp]這個陣列的意義表示編號為i的節點裡面儲存的是alp(0或者1)是孩子節點(存就儲存的是下一個節點的編號)

void insert(long
long a) { int x=0,alp;//x代表每個節點的標號 for(int i=31;i>=0;i--) { alp = (a>>i)&1; if(!son[x][alp]) son[x][alp] = ++cnt;//如果這個節點沒有孩子節點就建立它 x = son[x][alp];//將指標後移 //printf("[x=%d,alp=%d]\n",x,alp); } }

這樣一顆完美的字典樹就建立好了!,那麼怎麼查詢出異或的最大值呢?
查詢的話就相對來說比較簡單了!
迴圈輸入的n個數,每次找他的反方向,比如最開始的1,0(31)1這個32位二進位制數我們按照常理來說應該是先找1因為1和0(31)1的第一位是不同的,但是這個節點不存在就沒有辦法訪問到,我們只能在存在的分支上找和輸出的數不同的數,我們還是按照第一個輸入的這個十進位制1來說明,看下圖:
查詢過程


對應的查詢程式碼如下:

long long find(int a)
    {
        int x=0,alp;
        long long ret=0;
        for(int i=31;i>=0;i--)
        {
            alp = !((a>>i)&1);  //取反查詢
            ret<<=1;     //因為是按照位的,所以是*2
            if(son[x][alp]) x = son[x][alp],ret++;//如果和原來的那一為相反的存在的話,返回值就加上,並且在這個支路走
            else x = son[x][!alp];  //按照相同的順序找
            //printf("{x=%d,alp=%d,ret=%d}\n",x,alp,ret);
        }
        //printf("\n------\n");
        return ret;
    }

AC程式碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN = 100005;
long long a[MAXN];
struct Trie{
    int son[MAXN*13][3],cnt;//這裡必須是最大值*13+
    void init()
    {
        memset(son,0,sizeof son);
        cnt = 0;
    }
    void insert(long long a)
    {
        int x=0,alp;//x代表每個節點的標號
        for(int i=31;i>=0;i--)
        {
            alp = (a>>i)&1;
            if(!son[x][alp]) son[x][alp] = ++cnt;//如果這個節點沒有孩子節點就建立它
            x = son[x][alp];//將指標後移
            //printf("[x=%d,alp=%d]\n",x,alp);
        }
    }
    long long find(int a)
    {
        int x=0,alp;
        long long ret=0;
        for(int i=31;i>=0;i--)
        {
            alp = !((a>>i)&1);  //取反查詢
            ret<<=1;     //因為是按照位的,所以是*2
            if(son[x][alp]) x = son[x][alp],ret++;//如果和原來的那一為相反的存在的話,返回值就加上,並且在這個支路走
            else x = son[x][!alp];  //按照相同的順序找
            //printf("{x=%d,alp=%d,ret=%d}\n",x,alp,ret);
        }
        //printf("\n------\n");
        return ret;
    }
}trie;


int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        trie.init();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]),trie.insert(a[i]);
        long long  ans = -1;
        for(int i=1;i<=n;i++) ans = max(ans,trie.find(a[i]));
        printf("%lld\n",ans);
    }
    return 0;
}

如果不理解的話,可以把我註釋的程式程式碼,取消註釋就能直觀的反映變化

相關推薦

Find MaxXorSum 經典字典求異

題目大意 給出n個數,求在這n個數中取兩個數使得他們異或最大 解題思路 非常經典利用字典樹求異或的最大值。 讀懂這個題目之後,對於初學者我們可能會思考下面的問題: 1. 兩層迴圈o(n^2)肯定能求出了,但是肯定會超時 2. 這道題怎麼做呢?

Chip Factory (字典求異

  John is a manager of a CPU chip factory, the factory produces lots of chips everyday. To manage large amounts of products, every processor

Chip Factory(01字典求異

題目描述 John is a manager of a CPU chip factory, the factory produces lots of chips everyday. To manage large amounts of products, every p

poj 3764 字典求異

The xor-longest Path Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 4290 Accepted: 952 Description In an edge-weighted

HDU 4825 Xor Sum (字典求異)

#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int maxn=1e7+5; int ch[maxn][2]; in

POJ 3764 The xor-longest Path ( 字典求異 && 異自反性質 && 好題好思想)

strong span -s node poj printf return blog pre 題意 : 給出一顆無向邊構成是樹,每一條邊都有一個邊權,叫你選出一條路,使得此路所有的邊的異或值最大。 分析 : 暴力是不可能暴力的,這輩子不可能暴力,那麽來冷靜分析一下如何去

中國石油大學 Chip Factory(字典處理異)

9264: Chip Factory 時間限制: 5 Sec  記憶體限制: 128 MB 提交: 268  解決: 61 [提交] [狀態] [討論版] [命題人:admin] 題目描述 John is a manager of a CPU chip factory,

01字典專題 (解決異問題)不斷更新ing~

以前一直以為字典樹沒有多少用,但是最近一直碰到(難道是以前刷題太少的原因麼),其中有一類問題叫做01字典樹問題,它是用來解決xor的有力武器,通常是給你一個數組,問你一段連續的異或和最大是多少,正常思路貪心dp啥的都會一頭霧水,但是用01字典樹就能很快的解決,實現起來也十分

中國石油大學 Chip Factory(字典處理異)

9264: Chip Factory 時間限制: 5 Sec  記憶體限制: 128 MB 提交: 268  解決: 61 [提交] [狀態] [討論版] [命題人:admin] 題目描述 John is a

(01字典

#include<bits/stdc++.h> using namespace std; const int maxn = 100000 + 5; //集合中的數字個數 typedef long long LL; int ch[32 * maxn

二進位制trie解異問題

trie樹一般可以用於查詢與指定值最近的,而二進位制trie樹因為每個節點最多有兩個子節點,所以也可以查詢與指定值最遠的值,即異或最大值。 (1)問題:給定一個數組,在陣列中找到兩個數,使得這兩個數的異或值最大 (2)要點:對於陣列中的每個元素,二進位制trie樹查詢異

[二進制trie][貪心]CSUOJ1216異

include 貪心 targe printf IV 左右 class using ace 題目傳送門 過了好久,終於重新開始寫博客了。。。 這是一道二進制trie樹的模板題。 二進制trie樹,理解一下就是一顆二叉樹,左右兒子為0或1。 然後每插入一個數就進行一次

pre tmp 數字 markdown %d getchar using 整數 getch 1、1216: 異或最大值 http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1216 Time Limit: 2 Sec Me

Fast Arrangement (線段 維護區間 lazy標記)

Fast Arrangement 題意: 有一列火車同一時刻只能承載K個人,然後有Q個人要買票(給出Q組區間表示要買票的區間),(注意: 火車行駛區間為a-b的範圍),問那個人可以成功買到票(先到先得)

Fundraising【Gym - 101889F】【狀陣列+處理層層推進】

題目連結   哇哇哇!!!好題啊,昨晚比賽時一直卡在了第6組,當時爆零,極度尷尬……不過嘛,這都是ACMer的必經之路了,然後今早起來改了下,心態調整好,想了下,發現了處理問題的方式,然後就給過了。(其實昨晚上已經找到問題所在了,只是太急了,畢竟只有2個小時,剩下半小時的時候就

HDU-1754 I Hate It(線段,區間)

很多學校流行一種比較的習慣。老師們很喜歡詢問,從某某到某某當中,分數最高的是多少。  這讓很多學生很反感。  不管你喜不喜歡,現在需要你做的是,就是按照老師的要求,寫一個程式,模擬老師的詢問。當然,老師有時候需要更新某位同學的成績。 Input本題目包含多組測試,請處

[cqbzoj]區間異

奶牛異或 時間限制: 1 Sec 記憶體限制: 64 MB 題目描述 農民約翰在餵奶牛的時候被另一個問題卡住了。他的所有N(1 <= N <= 100,000)個奶牛在他面前排成一行(按序號1..N的順序),按照它們的社會等級排序。奶牛#

CSU_1216(異

題目簡述: 經典題目,求一個數組中兩個數異或運算的最大值。題目極其簡單,但是要求的複雜度需要達到O(N * log(N)),還是比較難的。 解題思路: 總的思路就是構建一棵0-1字典樹,然後一個數讓查詢一個與其異或結果最大的數的效率達到O(log(N)

Python中獲取字典中value的所對應的鍵的方法

可以用max(dict,key=dict.get)方法獲得字典dict中value的最大值所對應的鍵的方法,max(dict, key)方法首先遍歷迭代器,並將返回值作為引數傳遞給key對應的函式,然後將函式的執行結果傳給key,並以此時key值為標準進行大小判斷,返回最大值

線段求區間+區間更新+區間求和+lazy標記

馬上就省賽了,線段樹還缺個求區間最大值的板子,百度,Google全都是一個板子,而且還沒有lazy標記,樸素更新,好氣啊 區間最小值也同理 lazy的我只能自己寫 藍瘦香菇 code: #include<stdio.h> #include&