ACM-博弈论

本文深入探讨了博弈论中寻找必胜与必败点的方法,通过实例解析了单堆与多堆取物游戏的策略,介绍了nim-sum算法,并提供了代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先要明确明白博弈的基本思路是要寻找必败点与必胜点
必胜点:在这一点轮到我那我赢定了。性质:从必胜点出发一定会走到至少一个必败点(我在这里必胜是因为我走一步一定能让我的对手必败)。
必败点:我走到这一点我输定了。性质:从必败点出发前方全是必胜点(即我在必败点无论怎么走都是对面的必胜点)。
例1:15张牌,每次只能取1、3、4张牌,谁赢?
画表:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
P N P N N N N P N P N N N N P N
P表示必败点,N表示必胜点

简单例题:Hdoj-1847
AC-代码

#include<iostream>
#include<vector>
#include<map>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<stack>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
const int N = 1005;
int a[N];
int b[20];

int main()
{
    int n;
    b[0] = 1;
    for(int i=1;i<11;i++)
        b[i] = b[i-1]*2;//1000以内所有2的次幂
    while(scanf("%d",&n)==1){
        for(int i=1;i<=n;i++)
        for(int j=0;b[j]<=i;j++){
            if(a[i-b[j]]==0){//一个个减,减出来有必败点标记为必胜点
                a[i] = 1;
                break;
            }
        }
        if(a[n]==1) printf("Kiki\n");
        else printf("Cici\n");
    }
    return 0;
}

另一种问题:三堆里面或很多堆里面取。
必胜点是多堆数字异或非0的点
必败点是多堆数字异或为0的点
数字异或成为nim-sum,这也叫做nim-sum算法
可以用最后结果000来理解这一算法
例题:hdu1848 Fibonacci again and again

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
#define M 1050
int k,fibo[M],f[M];
int mex1(int p){
    int i,t,g[M]={0};//把g数组全部置0
    for(i=0;i<k;i++){//遍历前面所能走到的所有位置
        t=p-fibo[i];
        if(t<0)break;
        g[f[t]]=1;//能走到的标记为1
    }
    for(i=0;;i++){
        if(!g[i])return i;//返回从0开始未访问过的第一个值
    }
}
int main()
{
    int m,n,p,i,ans;
    fibo[0]=1,fibo[1]=2,i=1;
    while(fibo[i]<1050){
        fibo[i+1]=fibo[i-1]+fibo[i];
        i++;
    }
    k=i;//斐波拉及数的个数
    memset(f,-1,sizeof(f));
    f[0]=0;
    for(i=1;i<=1000;i++){
        f[i]=mex1(i);//算出每个数的值
    }
    while(scanf("%d%d%d",&m,&n,&p)!=EOF&&m+n+p){
        ans=0;
        ans^=f[m];ans^=f[n];ans^=f[p];//三个堆的nim-sum(异或)
        if(ans)printf("Fibo\n");
        else printf("Nacci\n");
    }
    return 0;

}

最后看一个例题:HDU 1849 Rabbit and Grass

#include<iostream>
#include<vector>
#include<map>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<stack>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
const int N = 1005;
int a[N];

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值