博弈论

巴什博弈Bash

 

 1堆n个石子,每次最少取一个,最多取m个

例如 m = 4

判断此刻先手状态(1为胜,0为败)
n = 00

n = 11

n = 21

n = 31

n = 41

n = 50

n = 61

n = 71

n = 81

n = 91

n = 100
example

 

必败态:当 n % (m + 1) == 0

 

尼姆博弈Nim

 

Nim

有n堆石子,每堆石子的数量分别是a1,a2,a3……,两个人依次从这些石子堆中选一堆,拿取任意的石子,至少一个,最后一个拿光石子的人胜利

 

必败态:每堆石子异或结果为0。

 

P2197 【模板】nim 游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

void solve() {
    int n;
    cin >> n;
    int ans = 0;
    for (int i = 0, x; i < n; i ++) {
        cin >> x;
        ans ^= x;
    }
    cout << ((ans) ? "Yes\n" : "No\n");
}
solve

 

栗酱的异或和 (nowcoder.com)

void solve() {
    int n, k;
    cin >> n >> k;
    int ans = 0, t;
    // 判断能否减少第 k 位的数字 使得异或和为零
    for (int i = 1, x; i <= n; i ++) {
        cin >> x;
        if (i == k) t = x;
        else ans ^= x;
    }
    // 至少要拿一颗石子 所以要严格大于异或和ans
    cout << ((ans < t) ? "Yes\n" : "No\n");
}
solve

 

是是非非 (nowcoder.com)

void solve() {
    int n, q;
    cin >> n >> q;
    ll ans = 0;
    vector <ll> a(n + 1);
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        ans ^= a[i];
    }
    ll x, y;
    while (q --) {
        cin >> x >> y;
        ans ^= a[x];
        a[x] = y;
        ans ^= y;
        if (ans) cout << "Kan\n";
        else cout << "Li\n";
    }
}
solve

 

取石子 (nowcoder.com)

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define qwq ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
const int N = 2e5 + 10, P = 998244353, INF = 0x3f3f3f3f;

int sg[110][110];
void getSG(int x, int y) {
    if (x >= 1 && !sg[x - 1][y]) sg[x][y] = 1;
    if (x >= 3 && !sg[x - 3][y]) sg[x][y] = 1;
    if (x >= 9 && !sg[x - 9][y]) sg[x][y] = 1;
    if (y >= 1 && !sg[x][y - 1]) sg[x][y] = 1;
    if (y >= 3 && !sg[x][y - 3]) sg[x][y] = 1;
    if (y >= 9 && !sg[x][y - 9]) sg[x][y] = 1;

}
void solve() {
    int n,m;
    cin >> n >> m;
    sg[0][0] = 0;
    for (int i = 0; i <= 100; i ++)
        for (int j = 0; j <= 100; j ++)
            getSG(i, j);
    
    cout << ((sg[n][m]) ? "win\n" : "lose\n");
}
int main() {
    qwq;
    int T = 1;
    // cin >> T;
    while (T--)
        solve();

    return 0;
}
打表

 

892. 台阶-Nim游戏 - AcWing题库

对于偶数台阶 想要移动到地面需要进行偶数次操作
每次拿x个石子到奇数台阶后 对方可以再拿x个至偶数台阶
所以偶数台阶对题目无影响

对于奇数台阶 所有奇数台阶满足经典Nim游戏 
异或和为0时 先手必败
void solve() {
    int n, ans = 0;
    cin >> n;
    for (int i = 1, x; i <= n; i ++) {
        cin >> x;
        if (i & 1) ans ^= x;
    }
    if (ans) puts("Yes");
    else puts("No");
}
solve

 

 

Nimk

有n堆石子,每堆石子的数量分别是a1,a2,a3……,两个人依次从这些石子堆中选 [1, k ] 堆,拿取任意的石子,至少一个,最后一个拿光石子的人胜利

 

必败态: 把所有数字(每堆石子的个数)转化成二进制后, 求每一位上1的个数num_i, 每一位都满足  num_i % (k + 1) == 0

 

sg函数

 

对于n个子游戏(比如n个都操作不了才算输)

我们可以用一个数字去表示他的状态  状态之间可以转移,就像nim游戏中a[i]个石头可以取走1~a[i]个

在nim游戏中 这样操作的a[i]可以变成0~a[i]-1中的任意情况 而这个数字 sg 就可以去表示它

 

 对于任意状态 x , 定义 SG(x) = mex(S),其中 S 是 x 后继状态的SG函数值的集合。如 x 有三个后继状态分别为 SG(a),SG(b),SG(c),那么SG(x) = mex{SG(a),SG(b),SG(c)}

SG(x)=0代表x为必败态,SG(x)!=0代表x为必胜态

求sg函数

int getSG(int x) {
    if (sg[x] != -1) 
        return sg[x];
    unordered_set<int> st;
    for (int i = 0; i < k; i ++)
        if (x >= a[i])
            st.insert(getSG(x - a[i])); // 后继状态
    for (int i = 0; ; i ++)
        if (!st.count(i)) return sg[x] = i;  // 求mex
}

 

 

打表方式

 

for (int i = 0; i <= 100; i ++)
  for (int j = 0; j <= 100; j ++)
    printf("sg[%d][%d] = %d\n", i, j, sg[i][j]);

 

for (int i = 0; i <= 100; i ++)
  cout << setw(3) << i << " \n"[i == 100];
for (int i = 0; i <= 100; i ++)
  cout << setw(3) << sg[i] << " \n"[i == 100];
 

 

for (int i = 1; i <= n; i ++) 
        cout << setw(2) << i << " \n"[i == n];
    for (int i = 1; i <= n; i ++) {
        cout << setw(2) << i << " ";
        for (int j = 1; j <= n; j ++)
            cout << setw(2) << sg[i][j] << " \n"[j == n];

 

posted @ 2023-08-18 13:30  匿名人士W  阅读(7)  评论(0编辑  收藏  举报