博弈论

如果一个状态可以到达必败态,那么这个状态就是必胜态,而初始状态sg[0]即无牌可抓就处于必败态,n的范围不大,可以递推处理所有状态

题目:HDU1847---Good Luck in CET-4 Everybody!

#include <bits/stdc++.h>
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Fov(i,x,y) for(int i=(x);i>=(y);--i)
#define Fo(i,x,y) for(int i=(x);i<(y);++i)
#define midf(a,b) ((a)+(b)>>1)
#define L(_) (_)<<1
#define R(_) ((_)<<1)|1
#define fi first
#define se second
#define ss(_) scanf("%s",_)
#define si(_) scanf("%d",&_)
#define sii(x,y) scanf("%d%d",&x,&y)
#define siii(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define sl(_) scanf("%lld",&_)
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>P;
inline int read()
{
    char ch=getchar(); int x=0, f=1;
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
    while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int n_max=1e3+10;
int f[15];
bool sg[n_max];

int main()
{
	//freopen("in.txt","r",stdin);
	f[0]=1;
	For(i,1,10) f[i]=f[i-1]*2;
	For(i,1,1e3) for(int j=0;f[j]<=i;++j) if(!sg[i-f[j]]) sg[i]=true;
	int n;
	while(~si(n)) printf("%s\n",sg[n]?"Kiki":"Cici");
	return 0;
}

一、巴什博奕

一堆石子,每一个人最少可以取一个石子,最多取m个石子。不能继续取石子的人游戏失败。

1.如果m>=n,明显先手胜

2.如果n%(m+1)=0,后手胜

3.如果n%(m+1)!=0,先手胜,且先手第一次取出n-n/(m+1)*(m+1)个

二、威佐夫博弈

有两堆若干个石子,数量分别为a、b,两人轮流取石子,可以从一堆当中取走任意多个或者从两堆当中同时取走相同多个石子(至少取走一个子)。最后取完的人获胜。

三、费波那契博弈

1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。

如果n属于斐波那契数列,则后手胜,否则先手胜

四、 Nim博弈

引入sg[x]=mex(sg[y],y为x所有能够到达的状态)(mex()函数是求序列中缺少的最小整数,如mex(0,1,3)=2)

当sg1^sg2^...^sgn=0时处于必败态,若为k!=0,则处于必胜态,且先手应将其中一个子游戏的sg值变为sg'=sg^k(HDU2176---取(m堆)石子游戏)

Key point:打表求sg函数,找规律

特例:从一堆中取出的个数任意,sg(x)=x;

题目1:La 5059---Play with Stones

#include<cstdio>
#include<cstring>
using namespace std;
const int n_max=100;
int sg[n_max];
bool vis[n_max];
int main()
{
    sg[1]=0;
    for(int i=2;i<=30;++i)
    {
        memset(vis,0,sizeof(vis));
        for(int j=1;j*2<=i;++j)
            vis[sg[i-j]]=true;
        for(int j=0;;++j)
        {
            if(!vis[j])
            {
                sg[i]=j;
                break;
            }
        }
        printf("%d ",sg[i]);
    }
    return 0;
}

1 0 2 1 3 0 4 2 5 1 6 3 7 0 8 4 9 2 10 5 11 1 12 6 13 3 14 7 15 

可以看出当n为偶数时,sg(n)=n/2;当把n为偶数的值全部删除后得到的数列与原数列一致,因而当n为奇数时sg(n)=sg(n/2)。

#include<cstdio>
using namespace std;
long long sg(long long x)
{
    return x&1?sg(x/2):x/2;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        long long a,res=0;
        scanf("%d",&n);
        for(int i=0;i<n;++i)
        {
            scanf("%lld",&a);
            res^=sg(a);
        }
        if(res) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

题目2:HDU 3032---Nim or not Nim?

#include<cstdio>
#include<cstring>
using namespace std;
const int n_max=100;
int sg[n_max];
bool vis[n_max];
int main()
{
    sg[0]=0;
    for(int i=1;i<=30;++i)
    {
        memset(vis,0,sizeof(vis));
        for(int j=1;j<=i;++j)
            vis[sg[i-j]]=true;
        for(int j=1;j<=i/2;++j)
            vis[sg[j]^sg[i-j]]=true;
        for(int j=0;;++j)
        {
            if(!vis[j])
            {
                sg[i]=j;
                break;
            }
        }
        printf("%d ",sg[i]);
    }
    return 0;
}

1 2 4 3 5 6 8 7 9 10 12 11 13 14 16 15 17 18 20 19 21 22 24 23 25 26 28 27 29 30 32 31 33 34 36 35 37 38 40 39 

可以看出当n%4=3时,sg(n)=n+1,当n%4=0时,sg(n)=n-1,其他情况sg(n)=n

#include<cstdio>
using namespace std;

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,res=0,a;
        scanf("%d",&n);
        for(int i=0;i<n;++i)
        {
            scanf("%d",&a);
            if(a%4==3) ++a;
            else if(a%4==0) --a;
            res^=a;
        }
        if(res)
            printf("Alice\n");
        else
            printf("Bob\n");
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值