博弈论

·巴什博奕
HDU 2188 & HDU 1846 
n个石子,每次取1~m个,最后一个取走的人获胜,问谁能获胜
只有当n是m+1的倍数(即n % (m + 1) == 0)的时候,后取者能够获胜
其他时候先取者第一次取完使得剩下的石子是m+1的倍数,每次后取者取i个,先取者就取(m+1-i)个,这样先取者一定自己能取最后一个

变形:条件不变,改为最后取光的人输。 结论:当(n-1)%(m+1)==0时后手胜利。

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n, m;
        scanf("%d %d", &n, &m);
        if(n % (m + 1) == 0) printf("Rabbit
");//后取者获胜
        else printf("Grass
");
    }
    return 0;
}
HDU 2149 博弈论 巴什博奕
除了谁赢还要输出如果先手想赢第一次应该取多少
先手第一次取完应使剩下的是m+1的倍数,因此取n % (m + 1)个
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
    int n, m;
    while(scanf("%d %d", &n, &m) != EOF)
    {
        if(n % (m + 1) == 0) printf("none
");
        else if(n < m)
        {
            for(int i = n; i < m; i++) printf("%d ", i);
            printf("%d
", m);
        }
        else
        {
            printf("%d
", n % (m + 1));
        }
    }
    return 0;
}
 
 
·威佐夫博弈
奇异局势即一方面对必输的局势,可以发现

几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。

可以看出任何自然数都包含在一个且仅有一个奇异局势中

 
POJ 1067 威佐夫博弈
两堆石子分别有a,b个,每次可以从一堆中拿走任意个,或者从两堆中都拿走任意个,最后一个取光的获胜
*解法:a =[k(1+√5)/2],b= a + k, 满足这两个式子的时候先取者会输
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int main()
{
    int a, b;
    while(scanf("%d %d", &a, &b) != EOF)
    {
        int k;
        if(a > b) swap(a, b);
        k = b - a;
        if(a == (int)((sqrt(5.0) + 1) * k / 2)) printf("0
");
        else printf("1
");
    }
    return 0;
}

HDU 2177 

威佐夫博弈,若能胜利,输出使得胜利的第一次取石子的结果

先预处理出所有的奇异局势,然后判断即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int SZ = 2000100;
int sg[SZ], h[SZ], a[SZ], f[SZ];
int vis[SZ];
void getf()
{
    int tmp = 0;
    memset(f, -1, sizeof(f));
    for(int i = 0; i <= 1000000; i++)
    {
        if(!vis[i] && !vis[i+tmp])
        {
            f[i] = i+tmp;
            f[i+tmp] = i;
            vis[i] = 1;
            vis[i+tmp] = 1;
            tmp++;
        }
    }
}
int main()
{
    int a, b;
    getf();
    while(1)
    {
        scanf("%d %d", &a, &b);
        if(a == 0 && b == 0) break;
        int ans = (int)((double)(b-a) * (1 + sqrt(5)) / 2 );
        if(ans == a) printf("0
");
        else
        {
            printf("1
");
            if(ans < a && f[ans] < b && f[ans] - ans == b - a) printf("%d %d
", ans, f[ans]);
            if(f[a] != ans && f[a] < b) printf("%d %d
", min(f[a],a), max(f[a], a));
            if(a != b && f[b] != ans && f[b] < a) printf("%d %d
", min(f[b],b), max(f[b],b));
        }
    }
    return 0;
}

·Nim博弈

P表示必败态,N表示必胜态

若一个状态是P,则它能转移到的所有状态都是N

若一个状态是N,则它能转移到的所有状态都是P

结论:当有n堆数量为a1 a2 a3...an的石子时i,它是P当且仅当a1^a2^a2^...^an==0

Nim博弈的变形:考虑到nim中石子数量其实就是最多能取石子的步数,最后的亦或和也就是最大步数的亦或和,我们可以去求下这个游戏中每堆石子最多能取的步数,这样就能转换为普通的nim了.求出每堆石子最多能取的步数即可

CodeForces 768E
nim博弈的变形,n堆石子中每堆中相同的石子数量只能取一次
每堆石子我们按1,2,3,4..n的顺序去取石子,当剩下的石子小于等于n的时候,就不能再取了,所以n就是最大步数,即sg函数的值
把每堆石子的最大步数^起来 变成nim博弈
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int main()
{
    int n;
    scanf("%d", &n);
    int flag = 0;
    while(n--)
    {
        int a;
        scanf("%d", &a);
        for(int j = 1; j <= 60; j++)
        {
            a -= j;
            if(a < 0) {flag ^= (j-1); break; }
        }
    }
    if(!flag) printf("YES
");
    else printf("NO
");
    return 0;
}

HDU 1536 S-Nim

也是nim博弈的变形,给出k个数,每次取的石子数只能在这k个数里

求SG函数,每次能取的石子数是f[i],f数组要排序哒

注意h函数值需要开到100。。因为开大了T到绝望T_T

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
using namespace std;
const int SZ = 10020;
int sg[SZ], f[110];
int h[101];
int n, k;
void init(int m)
{
    memset(sg, 0, sizeof(sg));
    for(int i = 0; i <= m; i++)
    {
        memset(h,0,sizeof(h));
        for(int j = 0; j < k; j++)
        {
            if(i < f[j]) break;
            h[sg[i-f[j]]] = 1;
        }
        for(int j = 0;; j++)
            if(!h[j])
            {
                sg[i] = j;
                break;
            }
    }
    return;
}
 char str[110];
int main()
{
    while(scanf("%d", &k))
    {
        if(!k) break;
        memset(f, 0, sizeof(f));
        for(int i = 0; i < k; i++) scanf("%d", &f[i]);
        sort(f, f+k);
        init(10000);
        int T;
        scanf("%d", &T);
        int idx = 0;
        while(T--)
        {
            scanf("%d", &n);
            int flag = 0, tmp;
            while(n--)
            {
                scanf("%d", &tmp);
                flag ^= sg[tmp];
            }
            if(flag) printf("W");
            else printf("L");
        }
        printf("
");
    }
    return 0;
}

·SG函数

https://www.cnblogs.com/ECJTUACM-873284962/p/6921829.html

模板:a记录的是每次取石子的方式

void init(int a[], int n)
{
    memset(sg, 0, sizeof(sg));
    for(int i = 0; i <= n; i++)
    {
        memset(h, 0, sizeof(h));
        for(int j = 1; j <= a[0]; j++)
        {
            if(i < a[j]) break;
            h[sg[i-a[j]]] = 1;
        }
        for(int j = 0; j <= n; j++)
            if(h[j] == 0)
            {
                sg[i] = j;
                break;
            }
    }
}

hdu 1848

三堆石子,每次能取的个数必须是斐波那契数,f数组就存斐波那契数

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int SZ = 1020;
int sg[SZ], h[SZ], a[SZ], f[SZ];
void init(int n)
{
    memset(sg, 0, sizeof(sg));
    for(int i = 1; i <= n; i++)
    {
        memset(h, 0, sizeof(h));
        for(int j = 1; j <= 20; j++)
        {
            if(i < f[j]) break;
            h[sg[i-f[j]]] = 1;
        }
        for(int j = 0;; j++)
            if(h[j] == 0)
            {
                sg[i] = j;
                break;
            }
    }
}

int main()
{
    f[0] = f[1] = 1;
    for(int i = 2; i <= 20; i++) f[i] = f[i-1] + f[i-2];
    init(1000);
    int n, m, k;
    while(1)
    {
        scanf("%d %d %d", &n, &m, &k);
        if(n == 0 && m == 0 && k == 0) break;
        if(sg[n] ^ sg[m] ^ sg[k]) printf("Fibo
");
        else printf("Nacci
");
    }
    return 0;
}

Codeforces 603C

丢题解 https://blog.csdn.net/huanghongxun/article/details/50197327

丢上还没看懂的SG

Codeforces 603C 
原文地址:https://www.cnblogs.com/pinkglightning/p/8392343.html