博弈論學習筆記

巴氏博弈

eg:hdu2149,2156

描述:只有一堆n個石子,每次能取1到m個物品,A先手,問誰能先取完石子

思路:這種問題的關鍵就在於能否取到關鍵點,比如,這裏只能能取到倒數m+2個石子,留下m+1個石子,則後者就必敗。

結論:如果初始石子是(m+1)的倍數,則先手必敗,否則先手必勝。

威佐夫博弈

eg:hdu2177,hdu5754

描述:有兩堆物品,每堆都有若干物品。每次可以從一堆中取任意物品,也可以從兩堆中取一樣多的任意個物品。先取光者獲勝。

思路:就是枚舉必敗態發現規律(最終思想sg函數差不多之後會介紹一下sg函數),可以枚舉前幾個必敗態,(0, 0), (1, 2), (3, 5), (4, 7), (6, 10), (8, 10)…..

最後得到結論:
ak=[k1+52]bk=ak+k

代碼判斷:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int x1, x2, temp;
    while(scanf("%d%d", &x1, &x2) != EOF) {
        if(x1 > x2) swap(x1, x2);
        temp = floor((x2 - x1) * (1 + sqrt(5.0)) / 2.0);
        if(temp == x1) cout << "後手贏" << endl;
        else cout << "先手贏" << endl;
    }
    return 0;
}

尼姆博弈

描述:有n堆石子,每堆有ai 個,兩人輪流從一堆中取任意個物品,誰把最後一個石子取光誰贏

結論:把所有石子個數異或起來,值爲0則先手敗,值爲1則先手勝。

代碼判斷:

#include<bits/stdc++.h>
using namespace std;
int a[11111];
int main()
{
    int n;
    while(scanf("%d", &n) != EOF) {
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            ans ^= a[i];
        }
        if(ans == 0) cout << "後手勝" << endl;
        else cout << "先手勝" << endl;
    }
    return 0;
}

eg:hdu1907這題需求和尼姆相反,取完最後一個的失敗,所以綜上所述,結論是,如果異或值是0,且所有數都小於2,則先手敗,如果異或值是1,且所有數都是1,也是先手敗。其他情況都是先手勝。

代碼實現:

#include<bits/stdc++.h>
using namespace std;
int a[11111];
int main()
{
    int n;
    while(scanf("%d", &n) != EOF) {
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        int ans = 0;
        int fa = 0;
        for(int i = 1; i <= n; i++) {
            ans ^= a[i];
            if(a[i] > 1) fa = 1;
        }
        if(ans == 0 &&  fa == 0 || ans != 0 && fa == 1) {
            cout << "先手敗" << endl;
        }
        else {
            cout << "後手敗" << endl;
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章