博弈問題總結(基礎篇)


博弈問題總結(基礎篇)

前言

最近做的博弈問題的題比較多,所以我就匯總了一下博弈問題的幾種題型,方便之后的做題

博弈論定義

博弈論就是指有若干個人進行一些對弈,並且默認每個人都是最聰明的,不會失誤,都可以找到當前的最優解,然后來尋找有沒有哪個人有必勝/必敗的的策略。

A、尼姆博弈

為什么叫尼姆博弈呢?因為這是尼姆(英文名:Nimm Game)發明的數學游戲。

博弈模型

有n堆各若干個物品,兩個人輪流從某一堆取任意多的物品,規定每次至少取一個,多者不限,最后取光者得勝。

分析

我們先考慮簡單的情況

1、n=1

這時先手必勝,因為他只需要把唯一的這一堆石子取走就可以了

2、n=2

若a[1]=a[2],先手必敗,因為無論先手在哪一堆石子中取走幾個,后手總能在另一堆石子中取走相同的個數

若a[1]!=a[2],我們假設a[1]>a[2],此時先手必勝,因為先手可以在第一堆石子中取走a[1]-a[2]個,這時兩堆石子的個數相同,下一次無論后手取走多少個,先手都可以在另一堆取走同樣多個,因此先手必勝

若a[1]<a[2],同上,先手必勝

3、要是n=3或者更大呢?

我們顯然不能像上面一樣去枚舉每種情況,所以我們要得出一個更為一般的結論

我們設總共有n堆石子,每一堆石子的個數分別為a[1]、a[2]、a[3]……a[n]

若a[1] ^ a[2] ^ a[3] ^ …… ^ a[n] =0先手必敗,反之先手必勝

下面是證明

如果異或和的最高位為i,那么必定有一堆石子的第 i 位為1

我們設這一堆石子的個數為k,其它所有石子的異或和為m,總異或和為x

則必定有k ^ m=x,我們把這一堆石子變成k^x

(k ^ x) ^ m=0

這時,所有石子的異或和都變成了0

舉個例子:11001 ^ 11100=00101,則有(11001 ^ 00101)^ 11100=0

如果當前所有數字的異或和為0,那么下一次無論你怎么取石子,異或和一定不會為0

這樣我們可以得出結論:如果先手異或和不為0,可以一步讓后手的情況為異或和為0;如果先手異或和為0,那么后手異或和就不為0

這樣,我們不斷進行游戲,最終一定會達到所有的數都為0的情況,而最后面對這種情況的一定會輸

所以我們可以得出結論:若a[1] ^ a[2] ^ a[3] ^ …… ^ a[n] =0先手必敗,反之先手必勝

例題

洛谷P2197模板題(好裸的板子

題意

甲,乙兩個人玩 Nim 取石子游戲。

Nim 游戲的規則是這樣的:地上有 n 堆石子(每堆石子數量小於 104),每人每次可從任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能從一堆里取。最后沒石子可取的人就輸了。假如甲是先手,且告訴你這 n 堆石子的數量,他想知道是否存在先手必勝的策略。

輸入格式

第一行一個整數 T(T≤10),表示有 T* 組數據

接下來每兩行是一組數據,第一行一個整數 n,表示有 n 堆石子,n≤10000

第二行有 n個數,表示每一堆石子的數量.

輸出格式

共 T行,如果對於這組數據存在先手必勝策略則輸出 Yes,否則輸出 No,每個單詞一行。

樣例

輸入

2
2
1 1
2
1 0

輸出

No
Yes
分析

沒什么好分析的,就是一個板子,直接上代碼

代碼(markdown寫代碼真不錯
#include<cstdio>
using namespace std;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        int ans=0;
        for(int i=1;i<=n;i++){
            int aa;
            scanf("%d",&aa);
            ans^=aa;
        }
        if(ans==0) printf("No\n");
        else printf("Yes\n");
    }
    return 0;
}

B、巴什博弈

博弈模型

一堆物品有n個,兩個頂尖聰明的人輪流從這堆物品中取物,規定每次至少取一個,最多取m個。最后取光者得勝。

分析

和尼姆博弈相比,巴什博弈只有一堆石子,而且加上了取的石子數的限制

如果石子數只有m個的話,那么先手必勝,因為它可以一次性把所有的石子取完

如果石子數有m+1個的話,那么先手必負,因為第一次先手無論取走多少石子,后手都可以一次性將其取完

如果石子數有m+1+n個(n<m)的話,那么先手在第一次可以取走n個石子,剩下的石子數為m+1,又回到了上面的這種情況,后手必負,先手必勝

我們不難發現,面臨m+1個石子的人一定失敗。

這樣的話兩個人的最優策略一定是通過拿走石子,使得對方拿石子時還有m+1個

我們再向一般情況推廣

當n=(m+1)\(\times\)k時,先手必負,因為對於每m+1個石子,先手取走k個后,后手總能取走v個,使得k+v=m+1,這樣,先手最終會面臨m+1的必負情況

當n\(\neq\)(m+1)\(\times\)k時,先手在第一次可以拿走一定的石子,使得n可以被(m+1)整除,這樣就轉變成了上面的情況

所以結論為:當n能整除m+1時先手必敗,否則先手必勝。

例題

1、HDU 1864 Brave Game

題意

十年前讀大學的時候,中國每年都要從國外引進一些電影大片,其中有一部電影就叫《勇敢者的游戲》(英文名稱:Zathura),一直到現在,我依然對於電影中的部分電腦特技印象深刻。
今天,大家選擇上機考試,就是一種勇敢(brave)的選擇;這個短學期,我們講的是博弈(game)專題;所以,大家現在玩的也是“勇敢者的游戲”,這也是我命名這個題目的原因。
當然,除了“勇敢”,我還希望看到“誠信”,無論考試成績如何,希望看到的都是一個真實的結果,我也相信大家一定能做到的~

各位勇敢者要玩的第一個游戲是什么呢?很簡單,它是這樣定義的:
1、 本游戲是一個二人游戲;
2、 有一堆石子一共有n個;
3、 兩人輪流進行;
4、 每走一步可以取走1…m個石子;
5、 最先取光石子的一方為勝;

如果游戲的雙方使用的都是最優策略,請輸出哪個人能贏。

輸入格式

輸入數據首先包含一個正整數C(C<=100),表示有C組測試數據。
每組測試數據占一行,包含兩個整數n和m(1<=n,m<=1000),n和m的含義見題目描述。

輸出格式

如果先走的人能贏,請輸出“first”,否則請輸出“second”,每個實例的輸出占一行。

樣例

樣例輸入

2
23 2
4 3

樣例輸出

first
second
分析

裸的板子,沒什么好說的

代碼
#include<cstdio>
using namespace std;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        int ans=n%(m+1);
        if(ans==0) printf("second\n");
        else printf("first\n");
    }
    return 0;
}

2、UDU 2188 悼念512汶川大地震遇難同胞——選拔志願者

題意

對於四川同胞遭受的災難,全國人民紛紛伸出援助之手,幾乎每個省市都派出了大量的救援人員,這其中包括搶險救災的武警部隊,治療和防疫的醫護人員,以及進行心理疏導的心理學專家。根據要求,我校也有一個奔赴災區救災的名額,由於廣大師生報名踴躍,學校不得不進行選拔來決定最后的人選。經過多輪的考核,形勢逐漸明朗,最后的名額將在“林隊”和“徐隊”之間產生。但是很巧合,2個人的簡歷幾乎一模一樣,這讓主持選拔的8600很是為難。無奈,他決定通過捐款來決定兩人誰能入選。
選拔規則如下:
1、最初的捐款箱是空的;
2、兩人輪流捐款,每次捐款額必須為正整數,並且每人每次捐款最多不超過m元(1<=m<=10)。
3、最先使得總捐款額達到或者超過n元(0<n<10000)的一方為勝者,則其可以親赴災區服務。
我們知道,兩人都很想入選志願者名單,並且都是非常聰明的人,假設林隊先捐,請你判斷誰能入選最后的名單?

輸入格式

輸入數據首先包含一個正整數C,表示包含C組測試用例,然后是C行數據,每行包含兩個正整數n,m,n和m的含義參見上面提到的規則。

輸出格式

對於每組測試數據,如果林隊能入選,請輸出字符串"Grass", 如果徐隊能入選,請輸出字符串"Rabbit",每個實例的輸出占一行。

樣例

樣例輸入

2
8 10
11 10

樣例輸出

Grass
Rabbit
分析

上一道題的代碼改改輸出就能過了

這兩道板子題主要是為了找自信

代碼
#include<cstdio>
using namespace std;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        int ans=n%(m+1);
        if(ans==0) printf("Rabbit\n");
        else printf("Grass\n");
    }
    return 0;
}

3、Ticket Game CodeForces - 1215D 博弈題

題目描述

Monocarp and Bicarp live in Berland, where every bus ticket consists of n digits (n is an even number). During the evening walk Monocarp and Bicarp found a ticket where some of the digits have been erased. The number of digits that have been erased is even.

Monocarp and Bicarp have decided to play a game with this ticket. Monocarp hates happy tickets, while Bicarp collects them. A ticket is considered happy if the sum of the first \(\frac{n}{2}\) digits of this ticket is equal to the sum of the last \(\frac{n}{2}\) digits.

Monocarp and Bicarp take turns (and Monocarp performs the first of them). During each turn, the current player must replace any erased digit with any digit from 0 to 9. The game ends when there are no erased digits in the ticket.

If the ticket is happy after all erased digits are replaced with decimal digits, then Bicarp wins. Otherwise, Monocarp wins. You have to determine who will win if both players play optimally.

輸入格式

The first line contains one even integer n (2≤n≤2⋅105) — the number of digits in the ticket.

The second line contains a string of n digits and "?" characters — the ticket which Monocarp and Bicarp have found. If the ii-th character is "?", then the ii-th digit is erased. Note that there may be leading zeroes. The number of "?" characters is even.

輸出格式

If Monocarp wins, print "Monocarp" (without quotes). Otherwise print "Bicarp" (without quotes).

樣例

Input

4
0523

Output

Bicarp

Input

2
??

Output

Bicarp

Input

8
?054??0?

Output

Bicarp

Input

6
???00?

Output

Monocarp
分析

一句話題意:一張票有n位數,如果這張票的前一半數字的和等於后一半數字的和(n一定是偶數),就稱這張票為快樂票。有些數被擦除了,標記為’?’(’?‘的個數也是偶數),現在Monocarp 和 Bicarp 進行一個游戲,兩人輪流將’?'變換成0到9的任意一個數,Monocarp先手,如果最后票為快樂票則Bicarp贏,否則Monocarp贏。

其實就是巴什博弈變了一下型,思想是一樣的

我們分情況來考慮一下

1、左邊的數字之和等於右邊的數字之和

這時,如果左右兩邊?的個數相等的話,后手贏,因為先手無論放什么數,后手都可以放一個相同的數來平衡

如果不相等,則先手必勝,因為最后肯定只能在一邊放,只要先手能放,就一定會打破平衡

2、左邊的數字之和小於右邊的數字之和

如果左邊?的個數大於等於右邊的個數,那么先手必勝,因為先手可以一直在左邊放9,后手只能不斷在右邊放9維持差距,但始終不能彌補差距

如果左邊?的個數小於右邊的個數,我們設左邊?的個數為a,右邊?的個數為b,那么前2a回合,策略同上;2a回合之后,先手放一個數x,后手唯一的選擇就是放一個y,使x+y=9,所以當左右兩邊數字之差為9的倍數時,后手勝,否則先手勝

3、右邊的數字之和小於左邊的數字之和(同上)

代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
char c[300000];
int lefw,rigw,lef,rigt;
int main(){
    int n;
    scanf("%d",&n);
    getchar();
    for(int i=1;i<=n/2;i++){
        scanf("%c",&c[i]);
        if(c[i]=='?'){
            lefw++;
        } else {
            lef+=(c[i]-'0');
        }
   }
   for(int i=n/2+1;i<=n;i++){
        scanf("%c",&c[i]);
        if(c[i]=='?'){
            rigw++;
        } else {
            rigt+=(c[i]-'0');
        }
   }
   if(lef==rigt){
        if(lefw==rigw) printf("Bicarp\n");
        else printf("Monocarp\n");
    }
    else if(lef>rigt){
       int cha=rigw-lefw;
       if(cha<=0){
            printf("Monocarp\n");
            return 0;
       }
       int chaa=lef-rigt;
       if(cha%2==0 && cha/2*9==chaa) printf("Bicarp\n");
        else printf("Monocarp\n");
    } else {
        int cha=lefw-rigw;
       if(cha<=0){
            printf("Monocarp\n");
            return 0;
       }
       int chaa=rigt-lef;
       if(cha%2==0 && cha/2*9==chaa) printf("Bicarp\n");
        else printf("Monocarp\n");
    }
    return 0;
}

C、威佐夫博弈

博弈模型

有兩堆各若干個物品,兩個人輪流從任一堆取至少一個或同時從兩堆中取同樣多的物品,規定每次至少取一個,多者不限,最后取光者得勝。

分析

我們先來考慮一下什么情況下先手必負

一、石子狀態為(0,0)

很顯然,先手必負

二、石子狀態為(1,2)

如果先手把左面那一堆石子全部拿走,那么后手可以把右面那一堆石子全部拿走,先手負

如果先手把右面那一堆石子全部拿走,那么后手可以把左面那一堆石子全部拿走,先手負

如果先手只在右面拿一個石子,那么后手可以一次性把兩堆石子都取走,先手負

如果先手在左面和右面各取一個石子,那么后手可以把右面的石子全部拿走,先手負

三、石子狀態為(3,5)

如果先手在左面的石子中取1個,那么后手可以在右面那一堆中取4個,這樣狀態就變成了(1,2),先手負

如果先手在左面的石子中取2個,那么后手可以在右面的石子中取3個,狀態又變成了(1,2),先手負

如果先手把左面的石子全部取完,那么后手也可以把右面的石子全部取完,先手負

如果先手在右面的石子中取1個,那么后手可以同時在兩堆石子中取2個,狀態又變成了(1,2),先手負

如果先手在右面的石子中取2個,那么后手可以同時在兩堆石子中取3個,先手負

如果先手在右面的石子中取3個,那么后手可以同時在兩堆石子中取1個,狀態又變成了(1,2),先手負

如果先手在右面的石子中取4個,那么后手可以在左面的石子中取1個,狀態又變成了(1,2),先手負

……

總而言之,這種情況先手負

那我們可以發現一個規律,下面的情況先手必負

第一種(0,0)

第二種(1,2)

第三種(3,5)

第四種 (4 ,7)

第五種(6,10)

第六種 (8,13)

第七種 (9 , 15)

第八種 (11 ,18)

……

第n種(a,b)

我們可以發現左右兩堆的差值是逐漸遞增的

而且左邊的數是之前沒有出現過的最小的自然數

而且更重要的是差值$\times$1.618=左邊的數

而且我們知道黃金分割數=\(\frac{\sqrt{5}-1}{2}\)=0.618

所以這個比例就是黃金分割數+1(是不是很神奇)

例題

HDU 1527 取石子游戲

題目描述

有兩堆石子,數量任意,可以不同。游戲開始由兩個人輪流取石子。游戲規定,每次有兩種不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在兩堆中同時取走相同數量的石子。最后把石子全部取完者為勝者。現在給出初始的兩堆石子的數目,如果輪到你先取,假設雙方都采取最好的策略,問最后你是勝者還是敗者。

輸入格式

輸入包含若干行,表示若干種石子的初始情況,其中每一行包含兩個非負整數a和b,表示兩堆石子的數目,a和b都不大於1,000,000,000。

輸出格式

輸出對應也有若干行,每行包含一個數字1或0,如果最后你是勝者,則為1,反之,則為0。

樣例

Input

2 1
8 4
4 7

Output

0
1
0
分析

模板題

代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int main(){
    int a,b;
    double r=(sqrt(5.0)+1)/2;
    while(scanf("%d%d",&a,&b)!=EOF){
        if(a>b) swap(a,b);
        int c=b-a;
		int now=c*r;
        if(now==a){
            printf("0\n");
        } else {
            printf("1\n");
        }
    }
    return 0;
}

總結

其實博弈問題只要掌握了模板,有了思路,解題就比較容易了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM