zoukankan      html  css  js  c++  java
  • 尼姆博弈

      问题一:有n堆物品各若干,两人轮流从某一对取任意多物品,规定每次至少取一个,多者不限,最后取光者胜利

      用(a,b,c)表示局势,若a xor b xor c==0,那么(a,b,c)为奇异局势,面对奇异局势必败。若(a,b,c)为非奇异局势,则只要将c变为a xor b即变成奇异局势

        

      若a1 xor a2 xor a3 ...  == 0 ,则称(a1,a2,a3......)为T态(利他态),反之称S态(利己态)

      定理1:S->T,只要将某数 - x 即可

      证明:设 c = a1 xor a2 xor a3 ... xor an > 0,且c的第最高位(二进制)1为p位,那么必存在at,其第p位也是1,令x=at xor c < at

      那么把原式的at 换成 x ,原式 = a1 xor a2 xor a3 ... x xor ... an = a1 xor x2 ... xor an xor c = 0; S->T转换完成 

      定理2:T->S,任何操作

      证明:反证法:若 c = a1 xor a2 xor a3 ... xor an = 0;

                且 c' = a1 xor a2 xor a3... at' xor ... an = 0;

                则 c xor c' = 0; 但是c xor c' = at xor at' !=0 矛盾

      

      定理3:S必赢

      证明:先手S->T(定理1),后手必有T->S(定理2),所以先手必将局势控制在S态

      定理4:T必败

      证明:定理(3)

      问题二:若某堆数量为1,则称孤单堆,若大于1,则为充裕堆。每个人 轮流在一堆中取若干根,最后取完为负

      定理:显然不存在T1,充裕堆不可能一次消去两个

      

      定理5:S0必败,T0必胜

      证明:S0即是奇数个孤单堆,每次只能取一根,每次第奇数根都由自己取,第偶数根都由对方取,所以必败

      

      定理6:S1态必胜

      证明:先手S1->S0,后手必败(定理5)

      定理7:S2≠>T0

      证明:充裕堆不可以一次消去两个

      定理8:S2->T2

      证明:S->T(定理1),不可能转换成T0(定理7),所以转换成T2态

      

      定理9:只有T2->S2/S1

      证明:T->S(定理2),不可能是S0

      定理10:S2 必胜

      证明:先手S2->T2(定理8),必有T2->S2/S1(定理9),所以先手可以将状态控制在S2,或者变成S1(必胜态)

      定理11:T2必输

      证明:定理10

      

      总结:第一题(最后取则胜)策略,先手将S1变为T0,那么每次轮到先手都是S0(奇数孤单堆),则必取最后一堆:

          S2->T2->S2->T2.......T2->S1-------------->T0->S0->T0->S0->T0....->S0->T0(全0)

         第二天(最后取则败)策略,先手将S1变为S0,那么每次轮到先手都是T0(偶数孤单堆),则对方必取最后一堆

          S2->T2->S2->T2.......T2->S1-------------->S0->T0->S0->T0....->S0->T0(全0) 

      所以要取胜就要取得S1,就要不断把T2留给对方,如果自己是T2,则必败

      例题hdu1907 (问题二模板)

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
        int t,n,a[10000];
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            int tmp=0;
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                if(a[i]>1) tmp++;
            }
            if(tmp==0){
                if(n%2==1) //S0态必败 
                    puts("Brother");
                else puts("John");
                continue;
            }    
            else if(tmp==1){//S1->S0,对方必败 
                puts("John");
                continue; 
            } 
            else {//S2必胜,T2必败 
                int tot=0;
                for(int i=1;i<=n;i++)
                tot^=a[i];
                if(tot!=0) puts("John");
                else puts("Brother");
            }
        }
    }

      hdu2509 问题一,但是要证明在该题情况下T2必会变成S2/S1,剩下的状况S1,S0,T0则不受影响

      证明:T状态必变成S,所以尽管对T2中的某一堆进行改变后,可能会产生新的两堆,但是局势仍是S

         at' = at1'+at2' 且at' != at, 则at != at1' xor at2' , 

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
        int t,n,a[1000];
        while(scanf("%d",&n)==1){
            int tmp=0;
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                if(a[i]>1) tmp++;            
            }
            if(tmp==0){
                if(n%2==0)//S0态必胜 
                    puts("Yes");
                else puts("No");
                continue; 
            }
            else if(tmp==1){//S1态必胜 
                puts("Yes");
                continue; 
            }
            else {
                int tot=0;
                for(int i=1;i<=n;i++) tot^=a[i]; 
                if(tot==0) puts("No");//T2必败 
                else puts("Yes"); 
            }
        }
        return 0;
    }
  • 相关阅读:
    LaTeX下的表格处理
    accumulate函数用法的坑
    将博客搬至CSDN
    linux; 文件名乱码;问价名出现问号
    vim打开多窗口、多文件之间的切换
    关于ssh-server
    更改Ubuntu gcc、g++默认编译器版本
    Elasticsearch nest实现类似Contains和Like功能
    Redis大幅性能提升之Batch批量读写
    Ext.NET 4.1 系统框架的搭建(后台) 附源码
  • 原文地址:https://www.cnblogs.com/zsben991126/p/10202023.html
Copyright © 2011-2022 走看看