基础博弈论题目

关于博弈论的博客:点击打开链接


看几遍博客再做题是极好的。


下面题的连接:http://acm.hust.edu.cn/vjudge/contest/126694  密码:swustacm


hdu1846

巴什博奕,看博客

#include 
using namespace std;
int main(void)
{
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m;
        scanf("%d%d",&n,&m);
        if(n%(m+1) == 0) puts("second");
        else             puts("first");
    }
    return 0;
}


hdu 2147

题意:给一个n*m的棋盘,右上角(1,m)有一个棋子,每次可以向左,向下,向左下的格子移动,谁不能动谁就输。就是(1,m)走到(n,1),

开始以为可以走任意多步,还以为是威佐夫博弈,结果只能走一步。这种情况根据最后的必败态往前面推就行了。

P:必败态。

N:必胜态。

基础博弈论题目_第1张图片

找出规律,P点的坐标都是奇数。

#include 

using namespace std;

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m) && (n||m)){
        if(n%2 && m%2) printf("What a pity!\n");
        else           printf("Wonderful!\n");
    }
    return 0;
}


hdu 2188

又是巴什博奕,题意要求是由n开始降,每次最多降m,最先降到0为胜,和从0开始一直升,最先升到n为胜是一个模型。

#include 

using namespace std;

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m;
        scanf("%d%d",&n,&m);
        if(n%(m+1)) puts("Grass");
        else        puts("Rabbit");
    }
    return 0;
}

hdu 2149

巴什博奕求第一次取的方案。

排除了输的情况(n%(m+1) == 0 )之后。输出方案,当m大于n的时候直接输出n--m就行了,m

#include 

using namespace std;

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m) != EOF){
        if(n%(m+1) == 0){
            puts("none");
            continue;
        }
        if(m >= n){
            for(int i = n;i < m;i++)
                printf("%d ",i);
            printf("%d\n",m);
        }
        else{
            printf("%d\n",n%(m+1));
        }
    }
    return 0;
}

hdu 1847

又是巴什博奕,我不会推,(这种不都是打表找规律吗

人家推的:点击打开链接

#include 

using namespace std;

void Init()
{
    int cf[10];
    cf[0] = 1;
    for(int i = 1;i < 8;i++) cf[i] = cf[i-1]*2;
    int a[1000] = {0};
    a[1] = a[2] = 1;
    for(int i = 1;i <= 100;i++){
        if(a[i] == 0){
            for(int j = 0;j < 8;j++)
                a[i+cf[j]] = 1;
        }
    }
    for(int i = 1;i <= 30;i++)
        printf("%d %d\n",i,a[i]);
}

int main()
{
    //Init();
    int n;
    while(scanf("%d",&n) != EOF){
        if(n%3 == 0) puts("Cici");
        else         puts("Kiki");
    }
    return 0;
}

hdu 1849

最简单的尼姆博弈,棋子的位置相当于石子的个数,移动就相当于取石子。

#include 

using namespace std;

int main()
{
    int n;
    while(scanf("%d",&n) && n){
        int ans = 0;
        for(int i = 1;i <= n;i++){
            int t;
            scanf("%d",&t);
            ans ^= t;
        }
        if(ans == 0) puts("Grass Win!");
        else         puts("Rabbit Win!");
    }
    return 0;
}

hdu 1850

题目是求尼姆博弈中先手能赢的前提下第一步能操作的方案数。

As we know,假如(a,b,c),其中一种方案是在c中取c-(a^b)(c>=(a^b))个(证明就看那个博客吧),那么针对每一种情况是不是只要a[i]>=a[1]^a[2]....a[i-1]^a[i+1]^a[n]就是一种可行方案了。可以先把所有a[i]异或起来可以降低复杂度。

#include 

using namespace std;

int main()
{
    int n;
    int a[110];
    while(scanf("%d",&n) && n){
        int tmp = 0;
        for(int i = 1;i <= n;i++){
            scanf("%d",&a[i]);
            tmp ^= a[i];
        }
        if(tmp == 0){
            printf("0\n");
            continue;
        }
        int ans = 0;
        for(int i = 1;i <= n;i++){
            tmp ^= a[i];
            if(a[i] >= tmp) ans++;
            tmp ^= a[i];
        }
        printf("%d\n",ans);
    }
    return 0;
}

hdu 1848

3堆石子,每次只能取fib个,问先手是否必胜。

sg函数的初等应用,只有1000,直接打表sg函数就行了,感觉这篇写的挺好的点击打开链接

#include 
using namespace std;
int a[10000];
int f[30];
void Init()          ///构造fib序列
{
    f[1] = 1;f[2] = 2;
    for(int i = 3;i < 30;i++)
    {
        f[i] = f[i-2]+f[i-1];
    }
}

int sg[2000];
int sg_init(int x)  ///记忆画搜索求sg函数
{
    if(sg[x] != -1) return sg[x];
    int vis[1005] = {0};
    for(int i = 1;f[i] <= x;i++)
        vis[sg_init(x-f[i])] = 1;
    for(int i = 0;;i++)
        if(!vis[i]) return sg[x] = i;
}


int main(void)
{
    Init();
    memset(sg,-1,sizeof sg);
    sg[0]=0;
    int n,m,p;
    while(scanf("%d%d%d",&n,&m,&p) && (n||m||p)){
        int ans = sg_init(n)^sg_init(m)^sg_init(p);
        if(ans == 0) puts("Nacci");
        else  puts("Fibo");
    }
    return 0;
}

hdu 1517

题意:两个人从1开始,轮流每个人在上一个人的基础上乘2-9的数,谁先超过n谁就赢。stan先手。

手动模拟能发现,1-9 stan胜,10-18 oll胜,19-162 stan胜,163-324 oll胜。

然后可以发现规律就是,轮流从1开始,乘9以内是stan胜,然后乘2是oll胜。然后就是手动模拟了。

别人写得更细一些:点击打开链接

#include 

using namespace std;

int main()
{
    int n;
    while(scanf("%d",&n) != EOF){
        int cnt = 0;
        int t = 1;
        int tmp = 0;
        while(t < n){
            if(tmp == 0) t *= 9;
            else         t *= 2;
            tmp = 1-tmp;
            cnt++;
        }
        if(cnt%2 == 1) puts("Stan wins.");
        else           puts("Ollie wins.");
    }
    return 0;
}

hdu 1536

集合s里面有k个数字。有m个nim游戏,规则是每次只能取s集合里面的数字个数的石子。先手赢输W,否则输出L。

输入,是先输入一个k,再输入k个数字代表s集合。然后输入一个m,代表m次nim游戏,每组输入一个n,然后输入n堆石子。


采用sg记忆画搜索做,和fib那个石子游戏基本上一样。

#include 
using namespace std;
const int N = 10000+10;
int k,n;
int SG[N];
int s[N];

int sg(int x)
{
    if(SG[x] != -1) return SG[x];
    int vis[100] = {0};  ///开100就够了
    for(int i = 1;i <= k && s[i] <= x;i++){
        vis[sg(x-s[i])] = 1;
    }
    for(int i = 0;;i++)
        if(!vis[i]) return SG[x] = i;
}

int main()
{
    while(scanf("%d",&k) && k){
        memset(SG,-1,sizeof SG);
        SG[0] = 0;
        for(int i = 1;i <= k;i++) scanf("%d",&s[i]);
        sort(s+1,s+1+k);
        int m;
        scanf("%d",&m);
        while(m--){
            scanf("%d",&n);
            int ans = 0;
            for(int i = 1;i <= n;i++){
                int t;
                scanf("%d",&t);
                ans ^= sg(t);
            }
            if(ans) printf("W");
            else    printf("L");
        }
        puts("");
    }
    return 0;
}

POJ 2311 

后面做的,但是我不会添加题目

题意:有一个n*m的贴纸,每次可以横着减,可以竖着减,先剪出1*1矩阵的赢

同样sg记忆画搜索。

我们可以发现的是当某一种状态是(1,x)或者(x,1)的时候是必胜的(一定能剪出1,1)。

sg[1][1]标记为必败态(输入大于2)。然后对于每个(n,m)点,可以转移到横着每一行剪,竖着每一行剪。但是有个问题是一定不要转移到只有一行和只有一列的情况,所以搜索的时候,从第二行开始,只转移到第n-2行就行了(列同样)。然后就记忆化搜索了。

#include 
#include 
#include 
#include 
using namespace std;
const int N = 200+10;
int n,m;
int SG[N][N];
int sg(int n,int m)
{
    if(SG[n][m] != -1) return SG[n][m];
    int vis[400] = {0};
    for(int i = 2;i < n-1;i++){
        vis[sg(i,m)^sg(n-i,m)] = 1;
    }
    for(int i = 2;i < m-1;i++){
        vis[sg(n,i)^sg(n,m-i)] = 1;
    }
    for(int i = 0;;i++)
        if(!vis[i]) return SG[n][m] = i;
}

int main(void)
{
    memset(SG,-1,sizeof SG);
    SG[1][1] = 0;
    while(scanf("%d%d",&n,&m) != EOF){
        if(sg(n,m)) puts("WIN");
        else         puts("LOSE");
    }
    return 0;
}


你可能感兴趣的:(博弈论)