組合博弈

(一)巴什博弈
只有一堆n個物品,兩個人輪流從這堆物品中取物,規定每次至少取一個,最多取m個。最后取光者得勝。顯然,如果n=m+1,那么由于一次最多只能取m個,所以,無論先取者拿走多少個,
后取者都能夠一次拿走剩余的物品,后者取勝。因此我們發現了如何取勝的法則:如n=(m+1)r+s,(r為任意自然數,s≤m),那么先取者要拿走s個物品,如果后取者拿走k(≤m)個,那么先者再拿走m+1-k個,結果剩下(m+1)(r-1)個,以后保持這樣的取法,那么先取者肯定獲勝。總之,要保持給對手留下(m+1)的倍數,就能最后獲勝。

這個游戲還可以有一種變相的玩法:兩個人輪流報數,每次至少報一個,最多報十個,誰能報到100者勝。##

取石子(一)

#include <cstdio>
int main()
{
     int t;
     scanf("%d",&t);
     int n,m;
     while(t--)
     {
         scanf("%d%d",&n,&m);
         if(n%(m+1)) printf("Win\n");
         else printf("Lose\n");
     }
}

Public Sale

#include <cstdio>
int main()
{
     int n,m;
     while(scanf("%d%d",&m,&n)!=EOF)
     {
         if(m>n)//這種情況先手非必敗的話只有一種出法
         {
             if(m%(n+1)) printf("%d\n",m%(n+1));
             else printf("none\n");
         }
         if(n>=m)//這種情況只需輸出大于等于m的就行
         {
             for(int i=m;i<n;i++)
                printf("%d ",i);
             printf("%d\n",n);
         }
     }
    return 0;
}

SG Version

#include<cstdlib>
#include<stdio.h>
#include<string.h>
using namespace std;
int fx[1010],g[1010];;
int n,m;
void getSG()
{
    memset(fx,0,sizeof(fx));
    fx[0]=0;//面對局面0,說明自己沒法取,所以局面0的結果是輸
    for(int i=1;i<=m;i++)//枚舉每個局面i
    {
        memset(g,0,sizeof(g));
        for(int j=1;j<=n;j++)//分解i局面,枚舉局面i產生的下一個局面,其中限制最多取n個,最少取1個
        {
            if(i>=j) g[fx[i-j]]=1;
            else break;//已經沒法取了
        }
        for(int j=0;j<=m;j++)//這里必須從0開始遍歷
        if(!g[j])
        {
            fx[i]=j;
            break;
        }
    }
}
int main()
{
    while(scanf("%d%d",&m,&n)!=EOF)
     {
         getSG();
         if(m>n)//這種情況先手非必敗的話只有一種出法
         {
             if(fx[m]) printf("%d\n",fx[m]);
             else printf("none\n");
         }
         if(n>=m)//這種情況只需輸出大于等于m的就行
         {
             for(int i=m;i<n;i++)
                printf("%d ",i);
             printf("%d\n",n);
         }
     }
   return 0;
}

Good Luck in CET-4 Everybody!
題意:
1、 總共n張牌;
2、 雙方輪流抓牌;
3、 每人每次抓牌的個數只能是2的冪次(即:1,2,4,8,16…)
4、 抓完牌,勝負結果也出來了:最后抓完牌的人為勝者;
每次都是Kiki先抓牌,請問誰能贏呢?

題解一:
(1)、若是留給Cici的是3,那么Cici只能取1個或2個,所以再輪到Kiki取的話必贏。
(2)、若是給Cici留下的是3的倍數,假設為3n(n=1,2,3,..),那么無論Cici取什么數,剩余的數一定可以寫成3m+1或者3m+2(m<n)的形式,那么只要Kiki再取的時候留給Cici的仍然是3的倍數的話,就必勝了。

#include <cstdio>
int main()
{
     int n;
     while(scanf("%d",&n)!=EOF)
     {
         if(n%3) printf("Kiki\n");
         else printf("Cici\n");
     }
    return 0;
}

題解二:
SG ( 嵌套循環 )

#include<cstdlib>
#include<stdio.h>
#include<string.h>
using namespace std;
const int MAXN=1010;
int fx[MAXN],g[MAXN];
int pow_2[15];
void getSG()
{
    fx[0]=0;
    for(int i=1;i<MAXN;i++)//枚舉每個局面i
    {
        memset(g,0,sizeof(g));
        for(int j=0;j<25;j++)//枚舉的局面i的分解區間
        {
            if(i-pow_2[j]>=0)
            {
                g[fx[i-pow_2[j]]]=1;
            }
            else break;
        }
        for(int j=0;j<MAXN;j++)
        {
            if(!g[j])
            {
                fx[i]=j;
                break;
            }
        }
    }
}
int main()
{
    pow_2[0]=1;
    for(int i=1;i<15;i++) pow_2[i]=pow_2[i-1]<<1;
    getSG();
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(fx[n]) printf("Kiki\n");
        else printf("Cici\n");
    }
}

題解三:
SG (記憶化搜索)

#include<cstdlib>
#include<stdio.h>
#include<string.h>
using namespace std;
const int MAXN=1010;
int fx[MAXN],g[MAXN];
int pow_2[15];
int getSG(int n)
{
    if(fx[n]!=-1) return fx[n];

    int g[MAXN];
    memset(g,0,sizeof(g));
    for(int j=0;j<25;j++)
    {
        if(n-pow_2[j]>=0)
        {
            getSG(n-pow_2[j]);
            g[fx[n-pow_2[j]]]=1;
        }
        else break;
    }
    for(int j=0;;j++)
    {
        if(!g[j])
        {
            fx[n]=j;
            break;
        }
    }
    return fx[n];
}
int main()
{
    pow_2[0]=1;
    for(int i=1;i<15;i++) pow_2[i]=pow_2[i-1]<<1;
    memset(fx,-1,sizeof(fx));
    fx[0]=0;//初始化
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(getSG(n)) printf("Kiki\n");
        else printf("Cici\n");
    }
}

題解四:

#include<cstdio>
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
       n%=(2+1);
       if(n==0) printf("Cici\n");//n=(b+1)*r,先手必輸
       else if(n==2) printf("Kiki\n");//n=b,先手一次性拿走b,先手贏
       else if(n&1) printf("Kiki\n");//n<b;則先手和后手每次只能拿走1
        //( 因為b^0=1 ),如果是奇數,則先手贏;反之,后手贏。
       else printf("Cici\n");
    }
    return 0;
}

https://scut.online/problem.php?id=93

題目描述


大家都知道小林桑和托爾的相遇其實是假酒造成的,在喝多了酒之后,小林桑不僅會說女仆的話題,作為一個程序員碼農她也會和別人玩數字有關的游戲,這次她和托爾玩的是這個游戲:首先我們提出兩個整數B,N (1 <=B,N <=109),從小林桑開始,兩個人輪流進行操作,操作的內容是:選取一個冪指數k(k≥0),使得Bk <= N,然后令N = N - B^k。如果輪到一個人的時候沒有可以選的冪指數kk了,這個人就輸了。給出B,N,你能預測出游戲的最終結果嗎?
輸入格式
在單個文件包含多組數據,請循環讀取至末尾。對于每組數據,包含了兩個整數B,N,以空格分割,獨占一行。
輸出格式
若小林桑獲勝,輸出"kobayashisan no kachi",獨占一行。若托爾獲勝,輸出"tohru no kachi",獨占一行。
樣例數據
輸入
2 37 3
輸出
tohru no kachi
kobayashisan no kachi

#include <bits/stdc++.h>
using namespace std;
int T,n,b;
int main()
{
   while(scanf("%d%d",&b,&n)!=EOF)
   {
        n%=(b+1);
        if(n==0) printf("tohru no kachi\n");//n=(b+1)*r,先手必輸
        else if(n==b) printf("kobayashisan no kachi\n");//n=b,先手一次性拿走b,先手贏
        else if(n&1) printf("kobayashisan no kachi\n");//n<b;則先手和后手每次只能拿走1
//( 因為b^0=1 ),如果是奇數,則先手贏;反之,后手贏。
        else printf("tohru no kachi\n");
   }
}

邂逅明下
題意:三個數字n,p,q,表示一堆硬幣一共有n枚,從這個硬幣堆里取硬幣,一次最少取p枚,最多q枚,如果剩下少于p枚就要一次取完。兩人輪流取,直到堆里的硬幣取完,最后一次取硬幣的算輸。對于每一行的三個數字,給出先取的人是否有必勝策略,如果有回答WIN,否則回答LOST。
題解:

若當前石子共有n =(p+q)* r個,則A必勝,必勝策略為:A第一次取q個,以后每次若B取K個,A取(p+q-k)個,如此下去最后必剩下p個給B,所以A必勝。
若n =(p+q)* r + left個(1< left <= p)B必勝,必勝策略為:每次取石子活動中,若A取k個,則B去(p+q-k)個,那么最后剩下left個給A,此時left <= p,所以A只能一次去完,B勝。
若n =(p+q)* r + left個(p < left <= q),則A必勝,必勝策略為:A第一次取t(1<left – t <= p)個,以后每次B取k個,則A取(p+q-k)個,那么最后留下1< left – t <=p給B,則A勝。

#include<cstdio>
int main()
{
    int n,p,q;
    while(~scanf("%d%d%d",&n,&p,&q))
    {
        n%=(p+q);
        if(n==0||n>p) printf("WIN\n");
        else printf("LOST\n");
    }
    return 0;
}

另外,可以通過觀察SG函數得出結果

#include<cstdlib>
#include<stdio.h>
#include<string.h>
using namespace std;
const int MAXN=65540;
int fx[MAXN],g[MAXN];
int n,p,q;
void getSG()
{
    fx[0]=1;//面對局面0,說明已經贏了
    for(int i=1;i<p;i++) fx[i]=0;//小于p個,必須自己取完,先手輸
    for(int i=p;i<=n;i++)
    {
        memset(g,0,sizeof(g));
        for(int j=p;j<=q;j++)
        {
            if(i-j>=0)
            {
                g[fx[i-j]]=1;
            }
        }
        for(int j=0;j<=n;j++)
        {
            if(!g[j])
            {
                fx[i]=j;
                break;
            }
        }
    }
    for(int i=0;i<=n;i++)//打印結果
    {
        if(fx[i]==0) printf("%d %d\n",i,fx[i]);
    }
}
int main()
{
    while(scanf("%d%d%d",&n,&p,&q)!=EOF)
    {
        getSG();
        if(fx[n]) printf("WIN\n");
        else printf("LOST\n");
    }
}

(二 )威佐夫博奕(Wythoff Game):有兩堆各若干個物品,兩個人輪流從某一堆或同
時從兩堆中取同樣多的物品,規定每次至少取一個,多者不限,最后取光者得勝。
我們用(ak,bk)(ak ≤ bk ,k=0,1,2,…,n)表示兩堆物品的數量并稱其為局勢,如果甲面對(0,0),那么甲已經輸了,這種局勢我們稱為奇異局勢。前幾個奇異局勢是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)(12,20)
可以看出,a0=b0=0,ak是未在前面出現過的最小自然數,而 bk= ak + k,奇異局勢有
如下三條性質:
1。任何自然數都包含在一個且僅有一個奇異局勢中。
由于ak是未在前面出現過的最小自然數,所以有ak > ak-1 ,而 bk= ak + k > ak-1 + k-1 =
bk-1 > ak-1 。所以性質1成立。
2。任意操作都可將奇異局勢變為非奇異局勢。
事實上,若只改變奇異局勢(ak,bk)的某一個分量,那么另一個分量不可能在其他奇異局勢中,所以必然是非奇異局勢。如果使(ak,bk)的兩個分量同時減少,則由于其差不變,且不可能是其他奇異局勢的差,因此也是非奇異局勢。
3。采用適當的方法,可以將非奇異局勢變為奇異局勢。
假設面對的局勢是(a,b),若 b = a,則同時從兩堆中取走 a 個物體,就變為了奇異局勢(0,0);如果a = ak ,b > bk,那么,取走b – bk個物體,即變為奇異局勢;如果a = ak , b < bk ,則同時從兩堆中拿走 ak – ab – ak個物體,變為奇異局勢( ab – ak , ab – ak+ b – ak);如果a > ak ,b= ak + k,則從第一堆中拿走多余的數量a – ak 即可;如果a < ak ,b= ak + k,分兩種情況,第一種,a=aj (j < k),從第二堆里面拿走 b – bj 即可;第二種,a=bj (j < k),第二堆里面拿走 b – aj 即可。 從如上性質可知,兩個人如果都采用正確操作,那么面對非奇異局勢,先拿者必勝;反之,則后拿者取勝。
那么任給一個局勢(a,b),怎樣判斷它是不是奇異局勢呢?我們有如下公式:
ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,…,n 方括號表示取整函數)
奇妙的是其中出現了黃金分割數(1+√5)/2 = 1。618…,因此,由ak,bk組成的矩形近似為黃金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],若a=[j(1+√5)/2],那么a = aj,bj = aj + j,若不等于,那么a = aj+1,bj+1 = aj+1+ j + 1,若都不是,那么就不是奇異局勢。然后再按照上述法則進行,一定會遇到奇異局勢。
https://vjudge.net/problem/HDU-1527

#include <cstdio>
#include<algorithm>
using namespace std;
int main()
{
   int a,b,k;
   while(scanf("%d%d",&a,&b)!=EOF)
   {
       if(a>b) swap(a,b);
       k=b-a;
       b=(int)(k*(1+sqrt(5))/2.0);
       if(a==b) printf("0\n");
       else printf("1\n");
   }
}

題意:在威佐夫博奕的基礎上新增加了一條要求:就是如果在贏得條件下,輸出第一步怎么走。如果在任意的一堆中取走石子能勝同時在兩堆中同時取走相同數量的石子也能勝,先輸出取走相同數量的石子的情況。
https://vjudge.net/problem/HDU-2177

#include <cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const double val=(1+sqrt(5))/2.0;
int main()
{
   int a,b,k,ak,bk,x,y;
   while(scanf("%d%d",&a,&b)!=EOF,a+b)
   {
       if(a>b) swap(a,b);
       k=b-a;
       ak=(int)(k*val);
       bk=ak+k;
       if(a==ak) printf("0\n");
       else
        {
            printf("1\n");
            if(abs(a-ak)==abs(b-bk)&&a>ak)
            {
                printf("%d %d\n",ak,bk);
            }
            for(int i=b-1;i>=0;i--)
            {
                int x=a,y=i;
                if(x>y) swap(x,y);
                int num=y-x;
                if((int)(num*val)==x)
                    printf("%d %d\n",x,y);
            }
       }
   }
}

(三 ) 尼姆博奕(Nimm Game):有三堆各若干個物品,兩個人輪流從某一堆取任意多的
物品,規定每次至少取一個,多者不限,最后取光者得勝。我們用(a,b,c)表示某種局勢,首先(0,0,0)顯然是奇異局勢,無論誰面對奇異局勢,都必然失敗。第二種奇異局勢(0,n,n),只要與對手拿走一樣多的物品,最后都將導致(0,0,0)。 任何奇異局勢(a,b,c)都有a(xor)b(xor)c =0。

題目1:

今有若干堆火柴,兩人依次從中拿取,規定每次只能從一堆中取若干根,
可將一堆全取走,但不可不取,最后取完者為,求先手勝負。
題解:設ans為所有數據的xor運算,如果ans=0,則必輸;否則,則贏。

題目2:

今有若干堆火柴,兩人依次從中拿取,規定每次只能從一堆中取若干根,
可將一堆全取走,但不可不取,最后取完者為,求先手勝負。
題解:全是1的時候,特判,數1的個數,奇數輸,偶數贏。否則,設ans為所有數據的xor運算,如果ans=0,則必輸;反之,則贏。

https://vjudge.net/problem/HDU-1907
題意:n堆石子,最后一個取完的人輸。
Nimm博弈:全是1的時候,特判,數1的個數,奇數輸,偶數贏。

#include <cstdio>
using namespace std;
int main()
{
   int t;
   scanf("%d",&t);
   while(t--)
   {
       int n,m,ans=0,flag=0;
       scanf("%d",&n);
       for(int i=0;i<n;i++)
       {
           scanf("%d",&m);
           ans^=m;
           if(m>1) flag=1;//當所有數據都為1時的特判
       }
       if(flag){
        if(ans==0) puts("Brother");
        else puts("John");
       }
       else
       {
           if(n&1) puts("Brother");
           else puts("John");
       }
   }
}

https://vjudge.net/problem/HDU-185
題意:二人小游戲:桌子上有M堆撲克牌;每堆牌的數量分別為Ni(i=1…M);兩人輪流進行;每走一步可以任意選擇一堆并取走其中的任意張牌;桌子上的撲克全部取光,則游戲結束;最后一次取牌的人為勝者。 現在我們不想研究到底先手為勝還是為負,我只想問大家:
――“先手的人如果想贏,第一步有幾種選擇呢?”

#include <cstdio>
const int maxn=110;
int arr[maxn];
int main()
{
    int n,res,sum;
    while(scanf("%d",&n)!=EOF,n)
    {
        res=0;
        sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",arr+i);
            res^=arr[i];
        }
        if(!res) {
            printf("0\n");
            continue;
        }
        //為了讓對手輸,則自己第一輪應該在某一堆拿走k塊石頭,使得對手面對奇異狀態,即對手的異或為0
        //第i堆可以取走石頭的前提是,使對手面對奇異狀態的石頭數<第i堆的石頭數
        for(int i=1;i<=n;i++)
        {
            if((res^arr[i])<arr[i]) sum++;//res^arr[i]:除了第i堆石頭,其它堆的石頭進行xor運算
        }
        printf("%d\n",sum);
    }
    return 0;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容

  • 有一種很有意思的游戲,就是有物體若干堆,可以是火柴棍或是圍棋子等等均可。兩個人輪流從堆中取物體若干,規定最后取光物...
    moosoo閱讀 674評論 0 0
  • 威佐夫博奕(Wythoff Game):有兩堆各若干個物品,兩個人輪流從某一堆或同時從兩堆中取同樣多的物品,規定每...
    碧影江白閱讀 1,221評論 0 0
  • super麗麗 馬麗打卡第40天 姓名:馬麗 公司:紹興市柯橋恰合紡織有限公司 《六項精進》課程: 第292期感謝...
    super麗麗閱讀 175評論 0 0
  • 早起 運動5k+ 練字無 英語單詞? 記賬 ? 說實話我是一個很簡單的人 我也才發現原來我是個職場菜鳥 很多事情別...
    Sawa123閱讀 197評論 0 0
  • 親愛的寶貝,今天是我們開始執行計劃的第一天,這注定是回歸的一天。回歸,意味著在自性的光芒下屬于你自己的那條充滿希望...
    小花fayer閱讀 335評論 0 1