• 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:
    ",q);
            for (i=0; i<m; ++i)
            {
                scanf("%d",&s);
                printf("%d
    ",query(s));
            }
            //desinfo(L);
        }
        return 0;
    }
  • 相关阅读:
    装饰器模式
    php单例模式
    php设计模式之工厂模式
    修改mysql密码
    [手游新项目历程]-37-用shell 脚本写守护进程
    消费经济学
    利用SetConsoleTextAttribute函数设置控制台颜色
    利用SetConsoleTextAttribute函数设置控制台颜色
    lua,修改字符串的某个字符
    lua,修改字符串的某个字符
  • 原文地址:https://www.cnblogs.com/Blackops/p/5932787.html
Copyright © 2020-2023  润新知