HDU 4825 Xor Sum(經典01字典樹+貪心)


Xor Sum

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others)
Total Submission(s): 1555    Accepted Submission(s): 657

Problem Description
Zeus 和 Prometheus 做了一個游戲,Prometheus 給 Zeus 一個集合,集合中包含了N個正整數,隨后 Prometheus 將向 Zeus 發起M次詢問,每次詢問中包含一個正整數 S ,之后 Zeus 需要在集合當中找出一個正整數 K ,使得 K 與 S 的異或結果最大。Prometheus 為了讓 Zeus 看到人類的偉大,隨即同意 Zeus 可以向人類求助。你能證明人類的智慧么?
 

 

Input
輸入包含若干組測試數據,每組測試數據包含若干行。
輸入的第一行是一個整數T(T < 10),表示共有T組數據。
每組數據的第一行輸入兩個正整數N,M(<1=N,M<=100000),接下來一行,包含N個正整數,代表 Zeus 的獲得的集合,之后M行,每行一個正整數S,代表 Prometheus 詢問的正整數。所有正整數均不超過2^32。
 

 

Output
對於每組數據,首先需要輸出單獨一行”Case #?:”,其中問號處應填入當前的數據組數,組數從1開始計算。
對於每個詢問,輸出一個正整數K,使得K與S異或值最大。
 

 

Sample Input
2
 
3 2
3 4 5
1
5
 
4 1
4 6 5 6
3
 

 

Sample Output
Case #1:
4
3
Case #2:
4
 

題目鏈接:HDU 4825

 

學習這個也學了挺久的,苦於能百度到的詳細資料甚少,尤其很多是用靜態數組開寫的(節省內存看起來有一點麻煩),好像這題沒看見用動態鏈表來寫的(目前還是入門階段就一直用着new或malloc寫),今天看了好一會兒的代碼終於看出一點頭緒了

題目本身非常經典,就是給一堆數(就記為 X吧)又給Q個詢問,每次又給出一個特定的數S,求(Xi^S)的最大值。

首先異或就是兩個二進制位置上的數不同就可以得到1相同則得到0,比如(10100)2^(01111)2=(11011)2,可以轉換成十進制驗證:20^15=27。

然后把每一個數都轉換成二進制(位置要對齊,前面不足則補0),轉換的長度就跟題目給的數據范圍有關了,此題就33位就夠了。更新字典樹的順序和過程與普通單詞字典樹一模一樣,從左到右地遍歷插入各位二進制數值,但顯然next指針只有兩個——next[0]與next[1],更新的最后把原來的值附到結尾節點的val上,表示這整條路對應的十進制是val。

然后如何求最大異或值呢,先要知道一般情況下正項等比數列前n-1項求和的值肯定要小於第n項的值,那也就是說最壞情況下走第x個節點而x+1~n均只能得到0的時候,也比不走x而x+1~n均得到1的情況好。

然后假設已經得到了最大異或值Max,那Max異或K就可以得到某個數Xi了,此時不知道Xi是什么,但是可以通過貪心找到它。

貪心從最高位也就是樹的開始位置直接往當前二進制位值取反的節點走,走到底那個節點的val就是我們要找的Xi,這里我直接用bitset來代替位運算直觀一點

代碼:

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define CLR(x,y) memset(x,y,sizeof(x))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=33;
struct info
{
    info *nxt[2];
    int val;
    info()
    {
        val=0;
        fill(nxt,nxt+2,nullptr);
    }
};
info *L;
void update(int n)
{
    bitset<N> bit=n;
    int i,indx;
    info *cur=L;
    for (i=32; i>=0; --i)
    {
        indx=bit[i];
        if(!cur->nxt[indx])
        {
            info *one=new info();
            cur->nxt[indx]=one;
            cur=one;
        }
        else
            cur=cur->nxt[indx];
    }
    cur->val=n;//末位置節點附着
}
int query(int k)
{
    bitset<N> bit=k;
    info *cur=L;
    int indx,i;
    for (i=32; i>=0; --i)
    {
        indx=bit[i];
        if(cur->nxt[indx^1])//往取反位置走
            cur=cur->nxt[indx^1];
        else
            cur=cur->nxt[indx];//沒有的話只能繼續往前走
    }
    return cur->val;
}
void desinfo(info *cur)//刪除字典樹釋放內存防止MLE
{
    for (int i=0; i<2; ++i)
        if(cur->nxt[i])
            desinfo(cur->nxt[i]);
    delete cur;
}
int main(void)
{
    int T,n,m,i,s,val;
    scanf("%d",&T);
    for (int q=1; q<=T; ++q)
    {
        L=new info();
        scanf("%d%d",&n,&m);
        for (i=0; i<n; ++i)
        {
            scanf("%d",&val);
            update(val);
        }
        printf("Case #%d:\n",q);
        for (i=0; i<m; ++i)
        {
            scanf("%d",&s);
            printf("%d\n",query(s));
        }
        //desinfo(L);
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM