博弈-组合游戏

组合游戏:

规则1:一个状态是必败的状态,当且仅当它的所有后继状态为必胜状态

        规则2:一个状态是必胜的状态,当且仅当它的所有后继状态中至少有一个是必败状态

1.Ferguson游戏:

两个盒子有石子n,m。游戏规则为选择其中一个盒子清空,把另一个盒子的石子拿一些给清空的盒子,但需保证至少都有一个。。终态为[1,1]

状态:
从后向前推,[n,m]状态能推出的有一个为必败,则[n,m]必胜
                        [n,m]能推出的全为必胜,则[n,m]必败


#include 
using namespace std;
#define maxn 100
int win[maxn][maxn];
int main(){
    win[1][1]=false;
    for(int k=3;k<20;k++){
        for(int n=1;n

Chomp!游戏:

有一个m×n的棋盘,每次可以取走一个方格并它右边和左边的所有棋子,取到最后拿到(1,1)的输


结论:除了(1,1)先手必败,其他情况必胜:

证明:

假设当前先手去了最右上角的那个方格,则后手选择了(k1,k2)进入了必胜状态,则显然先手刚开始也可以取(k1,k2)进入必胜状态。于是矛盾


约数游戏:

有1~n个数,两个人轮流取一个数并它所有的约数抹去,最后取完的胜;

显然先手一直必胜:

证明:假设先手先取了1,然后后手去了k进入必胜状态,显然先手开始也可以取k进入该必胜状态,因此矛盾


nim博弈:

结论:如果有x1^x2^...^xn=X,若X=0则必败,否则必胜

1.对于没有火柴的状态,显然是必败状态

2.对于必胜状态,必能推出必败状态:

证明:假设X不为0,且X的最高位(二进制)在第k位,显然xi中至少会有一个Y,第k位也为1(否则异或运算时,第k位的1没法获得),则第一次取的时候在Y上拿成Z=Y^X

显然Z

3.对于必败的状态,推出的都是必胜状态

证明:必败时,X=0,因为只能取一堆,因此无论怎么取,都只能使X非0;


组合游戏的和:

假设有K个组合游戏,G1 G2 G3  ... Gn。每次可以选择任一个进行,其他局面不变。

解决办法:SG函数和SG定理:

SG(x)=mex(S),S是x的后继状态的SG函数值集合,mex(S)表示不在S内的最小非负整数;

游戏和的SG函数等于各子游戏SG函数的NIM和


石子游戏:n堆石子,只能取 至少一个,且不能超过一半,最后不能取得输。


1.先算一下SG函数:


#include 
#include 
using namespace std;
#define maxn 110
int sg[maxn];
int vis[maxn];
int main(){
    sg[1]=0;
    for(int i=2;i

(从2开始)

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 

可以得出结论:

sg(n)=sg(n/2);n为奇数

sg(n)=n/2;n为偶数


于是得到如下程序:

#include 
#include 
using namespace std;
#define maxn 110
long long sg(long long x){
    return x%2==0? x/2:sg(x/2);
}
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        long long a,v=0;
        cin>>n;
        for(int i=0;i>a;
            v^=sg(a);
        }
        if(v)cout<<"yes"<

treblecross游戏


#include 
#include 
#include 
#include 
using namespace std;
int sg[202];//sg[i]表示连续的x个空格子组成的棋盘的SG值
int vis[202];
void init()
{
	int i,j,k;
	sg[0]=0;
	sg[1]=sg[2]=sg[3]=1;
	for(i=4;i<=200;i++)
	{
		memset(vis,0,sizeof(vis));
		for(j=3;j<=5&&i-j>=0;j++) vis[sg[i-j]]=1;
		for(j=6;i-j>=0;j++) vis[sg[j-5]^sg[i-j]]=1;
		for(j=max(i-5,0);j<=i-3;j++) vis[sg[j]]=1;
		for(j=0;j<=200;j++)
            if(!vis[j])
            {
                sg[i]=j;
                break;
            }
	}
}
int e[202],f[202],g[202],n;
int find()
{
    int s=0,i,j,k=0;
    for(i=0;i>T;
    while(T--)
    {
        cin>>a;
        int i,j,k,flag=0,t=0;
        n=strlen(a);
        for(i=0;i0&&a[i-1]=='X'&&a[i+1]=='X'){flag=1;e[t++]=i;}
            else if(i>1&&a[i-1]=='X'&&a[i-2]=='X'){flag=1;e[t++]=i;}
        }
        if(flag)
        {
            cout<<"WINNING"<=0&&j=0&&j


取石子(十)

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 6
描述

不知不觉取石子已经到第十道了。地球上的石子也快要取完了!

开个玩笑,当然还是有很多石子的,取石子的题目也是做不完的,今天又来了一道!

有n堆石子,每一堆的规则如下:

第一堆每次只能取2的幂次(即:1,2,4,8,16…);

第二堆只能取菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量,斐波那契数即后面的数是前面两个数的和);

第三堆可以取任意多个,最小1个,最多可以把这一堆石子取完;

第四堆只能取1和偶数个(即:1,2,4,6,8,10...);

第五堆只能取奇数个(即:1,3,5,7,9.....);

好吧,这样下去太烦人了,六堆及其以后每堆最多取的数量为堆数编号,即第六堆只能取(1,2,3,4,5,6),第七堆只能取(1,2,3,4,5,6,7)....

别看规则很多,但Yougth和Hrdv都是聪明人,现在由Yougth先取,比赛规定谁先取完所有石子既为胜者,输出胜者的名字。

输入
有多组测试数据,每组测试数据开始有一个n。
后面有n个数代表每一堆石子的数量,当n为0是表示输入结束。(所有数据小于1000)
输出
假如Yougth赢输出“Yougth”,Hrdv赢输出“Hrdv”。
样例输入
6
2 4 2 3 6 7 
样例输出
Hrdv



首先:第一石子的sg函数:

#include 
#include 
#include 
using namespace std;
#define maxn 100
int sg[maxn];
int vis[maxn];
int main(){
   sg[0]=0;
   cout<<0<<" ";
    for(int i=1;i

得出数据分析规律可知:

sg(i)=i%3;

同理程序计算第二堆

(因为规律较为复杂,于是直接用函数计算,存储下来)

第三堆sg函数为:

sg(i)=i;

第四堆sg函数:

if(i==1 || i==2)
      sg(i)= i;
else  sg(i)= ((i-2)/6)*3+(i-2)%6-1;

第五堆sg函数为:

sg(i)=i%2;

剩下的第k堆:

sg(i)=(i)%(k+1);


然后套用组合游戏的规律,把每堆的sg函数值进行NIM异或,得到sum,sum==0 后手赢 sum!=0 先手赢


于是可得以下程序:

#include 
#include 
#include 
using namespace std;
#define maxn 1010
int a[maxn];
int sg[maxn];
void fun1(){
    //int sg[maxn];
    int vis[maxn];
    int a[maxn];
    a[1]=1;a[2]=2;
    for(int i=3;i>n,n){
        for(int i=0;i>a[i];
        }
        int sum=0;
        for(int i=0;i





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