尼姆博弈

  问题一:有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;
}

 

posted on 2018-12-31 14:28  zsben  阅读(229)  评论(0编辑  收藏  举报

导航