zoukankan      html  css  js  c++  java
  • BZOJ 1022 [SHOI2008]小约翰的游戏John

    1022: [SHOI2008]小约翰的游戏John

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 1635  Solved: 1036
    [Submit][Status][Discuss]

    Description

    小约翰经常和他的哥哥玩一个非常有趣的游戏:桌子上有n堆石子,小约翰和他的哥哥轮流取石子,每个人取的时候,可以随意选择一堆石子,在这堆石子中取走任意多的石子,但不能一粒石子也不取,我们规定取到最后一粒石子的人算输。小约翰相当固执,他坚持认为先取的人有很大的优势,所以他总是先取石子,而他的哥哥就聪明多了,他从来没有在游戏中犯过错误。小约翰一怒之前请你来做他的参谋。自然,你应该先写一个程序,预测一下谁将获得游戏的胜利。

    Input

    本题的输入由多组数据组成,第一行包括一个整数T,表示输入总共有T组数据(T≤500)。每组数据的第一行包括一个整数N(N≤50),表示共有N堆石子,接下来有N个不超过5000的整数,分别表示每堆石子的数目。

    Output

    每组数据的输出占一行,每行输出一个单词。如果约翰能赢得比赛,则输出“John”,否则输出“Brother”,请注意单词的大小写。

    Sample Input

    2
    3
    3 5 1
    1
    1

    Sample Output

    John
    Brother

    HINT

    【数据规模】

    对于40%的数据,T ≤ 250。

    对于100%的数据,T ≤ 500。

    Source

    Seerc2007

    题解:系统的学了一下博弈论。。。妈妈呀。。。

    具体去膜贾志豪的集训队论文好了。。。

    对于这道题,zidaneandmessi

    ①石子数全是1的时候,是最简单的情况。堆数为奇数后手必胜,为偶数先手必胜。

    ②只有两堆石子数不为1,且它们的石子数相等,后手必胜,理由如下:

    (1)先手若取走一堆,后手可以取走另一堆或者剩下一个,回到状态①。

    (2)先手若取一堆剩一个,后手可以取走另一堆或剩一个,回到状态①。

    (3)先手若取一堆剩下多于一个,后手可以在另一堆中取走相同个数的石子,重复状态②(3),直到两堆都只剩两个石子,先手必须选择②(1)或②(2)。

    ③只有两堆石子数不为1,且它们的石子数不相等。先手必胜。先手可以从多的一堆里取,使两堆石子相等,回到状态②。

    ④剩下偶数堆,由多对相等且不为1的石子堆组成。后手必胜。无论先手如何取,后手都在配对的堆中取相同个数的石子,这样可以使相等的堆两两消去,最后转为状态②。

    ⑤剩下偶数堆。由同样的多对不相等且不为1的石子堆组成。后手必胜。后手可以用③的方法每次使配对的两堆石子数相等,转为状态④。

    ⑥剩下部分是同样的多对不相等且不为1的石子堆,其余堆石子数都相等。

    (1)相等的堆数为偶数,后手必胜。后手可以按⑤的方法转到状态②获胜。

    (2)相等的堆数为奇数,先手必胜。先手从相等的拿走一堆后就变成了⑥(1)。

    行了,就此打住!如果再这么写下去,恐怕是无止境了。写到这里,我们发现后手总是尽可能地制造配对,并想方设法地回到他最擅长的状态②。但是知道了这个并没有多大用处,因为对于抽象的问题,还是无法下手。

    但是我们发现了,如果能配对成功,a1^a2^...^an=0这个等式确实是成立的,因为以异或运算满足交换律和结合律,把配对的两个异或结果算出之后,就全变成0了。

    如果a1^a2^...^an结果不为0呢?那么后手就倒霉了。因为先手总能找出一种拿石子的方式,把异或变成0。设结果为x,则每一个ai异或x后肯定有增有减,不可能全都增加。至少,为结果x提供最高位1的那个ai异或x后最高位变0了,一定是减少的。只要先手把它拿成剩下x个,新局面的异或结果就是0了。

    那么后手有没有翻盘的可能呢?答案是否定的。因为一旦异或结果变成0后,再拿只会让异或结果不再是0。因为如果两次都是0了,设这次拿走石子把ai=m变成了ai=n(0<n<m),a1^a2^…^a(i-1)^a(i+1)^…^an的运算结果设为s,则s^n=0,s^m=0,说明n=m=s,与n<m矛盾,这样后手不论再怎么拿,都不能避免失利。

    另外,如果结果为0,但是不能配对怎么办?很简单,根据上面的结论异或结果为0和不为0是交替变化的,只要先手每次移动使结果不为0,后手就再让结果为0就可以了,这样下去直到石子剩得不多了,胜负就很明显了。所以,后手一旦失误,先手就会使局势扭转!看似随意的游戏,实际上步步为营啊!

    还没完,我们推导了半天把孤零零的状态①给忘了。注意到②有个条件是相等两堆石子数不为1,所以如果所有石子数都是1,直接套用①的结论即可。

    代码们:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<queue>
     6 #include<cstring>
     7 #define PAU putchar(' ')
     8 #define ENT putchar('
    ')
     9 using namespace std;
    10 inline int read(){
    11     int x=0,sig=1;char ch=getchar();
    12     while(!isdigit(ch)){if(ch=='-')sig=-1;ch=getchar();}
    13     while(isdigit(ch))x=10*x+ch-'0',ch=getchar();
    14     return x*=sig;
    15 }
    16 inline void write(int x){
    17     if(x==0){putchar('0');return;}if(x<0)putchar('-'),x=-x;
    18     int len=0,buf[15];while(x)buf[len++]=x%10,x/=10;
    19     for(int i=len-1;i>=0;i--)putchar(buf[i]+'0');return;
    20 }
    21 void init(){
    22     int x,t,n,T;bool f=true;
    23     T=read();
    24     while(T--){
    25         n=read();f=true;t=0;
    26         for(int i=1;i<=n;i++){
    27             x=read();
    28             if(x!=1)f=0;
    29             t^=x;
    30         }
    31         if(f&&(n&1))puts("Brother");
    32         else if(f)puts("John");
    33         else if(t)puts("John");
    34         else puts("Brother");
    35     }
    36     return;
    37 }
    38 void work(){
    39     return;
    40 }
    41 void print(){
    42     return;
    43 }
    44 int main(){init();work();print();return 0;}
  • 相关阅读:
    Redhat6.4安装MongoDBv3.6.3
    windows模糊查询指定进程是否存在
    Linux普通用户不能使用TAB键、上下键
    零基础Python爬虫实现(百度贴吧)
    零基础Python爬虫实现(爬取最新电影排行)
    让bat批处理后台运行,不显示cmd窗口(完全静化)
    根据进程名监控进程(邮件提醒)
    android 开发中,必须要注意的知识点. (持续更新)
    Android上传文件至服务器
    为应用添加多个Activity与参数传递
  • 原文地址:https://www.cnblogs.com/chxer/p/4655128.html
Copyright © 2011-2022 走看看