各種常見的博弈論 + 簡單例題

斐波那契博弈
有一堆個數爲n(n>=2)的石子,遊戲雙方輪流取石子,規則如下:
1)先手不能在第一次把所有的石子取完,至少取1顆;
2)之後每次可以取的石子數至少爲1,至多爲對手剛取的石子數的2倍。
約定取走最後一個石子的人爲贏家,求必敗態。
結論:當n爲Fibonacci數的時爲必敗態。
即將斐波那契數列打表再判斷n是否爲斐波那契數判斷即可

例題:HDU - 2516 取石子游戲
(我這裏打表了,TIME是0 哦豁)

#include <bits/stdc++.h>
#define endl '\n'
#define INF 1e9+7
#define ll long long
#define ull unsigned long long
using namespace std;

long long feibonaq[100]={
1,
2,
3,
5,
8,
13,
21,
34,
55,
89,
144,
233,
377,
610,
987,
1597,
2584,
4181,
6765,
10946,
17711,
28657,
46368,
75025,
121393,
196418,
317811,
514229,
832040,
1346269,
2178309,
3524578,
5702887,
9227465,
14930352,
24157817,
39088169,
63245986,
102334155,
165580141,
267914296,
433494437,
701408733,
1134903170,
1836311903,
2971215073,
4807526976,
7778742049,
12586269025,
20365011074,
32951280099,
53316291173,
86267571272,
139583862445,
225851433717,
365435296162,
591286729879,
956722026041,
1548008755920,
2504730781961,
4052739537881,
6557470319842,
10610209857723,
17167680177565,
27777890035288,
44945570212853,
72723460248141,
117669030460994,
190392490709135,
308061521170129,
498454011879264,
806515533049393,
1304969544928657,
2111485077978050,
3416454622906707,
5527939700884757,
8944394323791464,
14472334024676221,
23416728348467685,
37889062373143906,
61305790721611591,
99194853094755497,
160500643816367088,
259695496911122585,
420196140727489673,
679891637638612258,
1100087778366101931,
1779979416004714189,
2880067194370816120,
4660046610375530309,
7540113804746346429};

int main() {
    ios::sync_with_stdio(false);
    ll n;
    while(~scanf("%lld",&n)) {
        if(n==0) break;
        int flag=0;
        if(n<=2971215073) {
            for(int i=1;i<=45;i++) {
                if(n<feibonaq[i]) break;
                if(n==feibonaq[i]) {
                    flag++;
                    break;
                }
            }
        }
        else {
            for(int i=46;i<=90;i++) {
                if(n<feibonaq[i]) break;
                if(n==feibonaq[i]) {
                    flag++;
                    break;
                }
            }
        }
        if(flag==1) printf("%s\n","Second win");
        else printf("%s\n","First win");
    }
    return 0;
}

巴什博弈
只有一堆n個物品,兩個人輪流從中取物,規定每次最少取一個,最多取m個,最後取光者爲勝。
結論:一輪最多拿的就是1+m個,所以控制下去,最後的不到(1+m)的物品肯定會被後手拿到的。
即判斷n能不能除盡m+1,如若能則後手必勝

例題:HDU - 1846 Brave Game

#include <bits/stdc++.h>
#define endl '\n'
#define INF 1e9+7
#define ll long long
#define ull unsigned long long
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        int n,m;
        cin>>n>>m;
        //如果m+1>=n,則後手必勝,說明當是這種情況的倍數時,後手能夠一直製作出這種情況,則後手必勝
        if(n%(m+1)==0) cout<<"second"<<endl;
        else cout<<"first"<<endl;
    }
    return 0;
}

尼姆博弈
有任意堆物品,每堆物品的個數是任意的,雙方輪流從中取物品,每一次只能從一堆物品中取部分或全部物品,最少取一件,取到最後一件物品的人獲勝。
結論:把每堆物品數全部異或起來,如果得到的值爲0,那麼先手必敗,否則先手必勝。

例題:POJ - 2234 Matches Game

#include <iostream>
//#include <bits/stdc++.h>
#define endl '\n'
#define INF 1e9+7
#define ll long long
#define ull unsigned long long
using namespace std;

int k[100];

int main() {
    ios::sync_with_stdio(false);
    int n;
    while(cin>>n) {
        int tmp=0;
        for(int i=0;i<n;i++) {
            cin>>k[i];
            tmp^=k[i];
        }
        if(tmp==0) cout<<"No"<<endl;
        else cout<<"Yes"<<endl;
    }
}

乘法博弈
2 個人玩遊戲,從 1 開始,輪流對數進行累乘,直到超過一個指定的值m時,該玩家獲勝。
如果乘數範圍是2~n,
則如果m爲2~n,則先手必勝。
如果是n+1~2×(上一個後範圍),則後手必勝,因爲無論第一次先手乘的數是什麼,數必在2到n之間
結論:必勝態對稱,於是將m不斷除與2n,判斷餘數在2到n還是n+1到2n之間即可

例題:POJ - 2505 A multiplication game

#include <iostream>
//#include <bits/stdc++.h>
#define endl '\n'
#define INF 1e9+7
#define ll long long
#define ull unsigned long long
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    long double n;
    while(cin>>n) {
        while(n>18) {
            n/=2;
            n/=9;
        }
        if(n<=9) cout<<"Stan wins."<<endl;
        else cout<<"Ollie wins."<<endl;
    }
}

環形博弈
n個石子圍成一個環,每次取一個或者取相鄰的2個。
證明:以後再寫(
結論:石子數目小於等於2 先手勝,其他 後手勝。

例題:POJ - 2484 A Funny Game

#include <iostream>
//#include <bits/stdc++.h>
#define endl '\n'
#define INF 1e9+7
#define ll long long
#define ull unsigned long long
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    int n;
    while(cin>>n&&n!=0) {
        if(n<=2) cout<<"Alice"<<endl;
        else cout<<"Bob"<<endl;
    }
}

對稱博弈
n個石子圍成環,每次只能取相鄰的1 - k個
結論:如果k<n:對k=1,如果n能被2整除,則後手贏
如果k>1,後手贏(先手取什麼位置後手就取對稱的位置,這樣保證後手永遠能取到)
如果k>=n:先手贏

例題:HDU - 3951 Coin Game

#include <bits/stdc++.h>
#define endl '\n'
#define INF 1e9+7
#define ll long long
#define ull unsigned long long
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    for(int i=1;i<=t;i++) {
        int n,k;
        cin>>n>>k;
        cout<<"Case "<<i<<": ";
        if(k<n) {
            if(k==1) {
                if(n%2==0) cout<<"second"<<endl;
                else cout<<"first"<<endl;
            }
            else cout<<"second"<<endl;
        }
        else cout<<"first"<<endl;
    }
}

威佐夫博弈
有兩堆各若干的物品,兩人輪流從其中一堆取至少一件物品,至多不限,或從兩堆中同時取相同件物品,規定最後取完者勝利。
結論:看兩個數的差值t是不是滿足 (sqrt(5)+1)/2*t==min(n1,n2);,是的話則後手必勝

例題:HDU - 1527 取石子游戲

#include <bits/stdc++.h>
#define endl '\n'
#define INF 1e9+7
#define ll long long
#define ull unsigned long long
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    int a,b;
    double temp;
    while(cin>>a>>b) {
        if(a>b) swap(a,b);
        temp=floor((b-a)*(1+sqrt(5.0))/2.0);
        if(temp==a) cout<<0<<endl;
        else cout<<1<<endl;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章