博弈不完全总结

一.巴什博奕(Bash Game)
一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取1 or p个,最多取m个。最后取光者得胜.
eg:m=4
n=4 A必胜
n=5 A 不论先拿了几个,B 都可以一次性把剩下的拿完,A 必输
n=6 A 可以先拿 1 个,然后数量变成了 5 个,此时问题就变成了n=5.B 无论拿几个,A 都可以一次性把剩下的拿完.A必胜.
n=7 8 9 同上
n=10 同n=5 A 必输

n 是(m+1)的倍数,那么 先手者就必输。
验证如下:
如果物品数量 n 可以化成 k(m+1)+ x 的形式(即 n 不可被(m+1)整除)其中 k 为自然数,0 < x < m+1 , 那么只要先手者第一次拿走 x 个物品,剩余物品变为 k(m+1),接下来无论后手者怎么拿,先手者都可以让物品数量变 k(m+1) , k逐渐变小直到最后变为 1 ,此时剩余物品为 m+1 ,接下来无论后手者拿多少都拿不完 ,并且会使剩余物品数量小于 m+1 ,而先手者则可以直接把剩下的拿完。所以先手者必胜。
如果物品数量为 k(m+1)(即 n 可被(m+1)整除)此时先手者就会面临上面的 后手者的问题,所以先手者必输。

#include <iostream>
#include <algorithm>
#include<string>
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)  //后者胜
			puts("no");
		else
			puts("yes");  //前者胜
	}
	return 0;
}

n= k(m+p)+ x

反巴什博奕:最后取光者输
(n-1)%(m+1)==0 则 后手胜利

借鉴
待补充类型

二.威佐夫博奕(Wythoff Game)
有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

若两堆物品的初始值为(x,y),且x<y,则另z=y-x;
记w=(int)[((sqrt(5)+1)/2)*z ];
若w=x,则先手必败,否则先手必胜。

#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int main()
{
    int a,b,k,zz;
    while(scanf("%d%d",&a,&b)==2){
        k=abs(a-b);
        a=a<b?a:b;
        zz=ceil(k*(1.0+sqrt(5.0))/2);
        if(a==zz)
            printf("no\n");
        else
            printf("yes\n");
    }
}

三. 尼姆博弈(Nimm Game)
有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获胜。

结论:把每堆物品数全部异或起来,如果得到的值为0,那么先手必败,否则先手必胜。

第一个类型:就是让其判断胜利的人//最后取完者赢
对n堆石子求异或和,根据当Nim和!= 0时,先手胜利,否则失败就能判断出来。

#include <iostream>
#include <iostream>
#include <algorithm>
#include<string>
using namespace std;
int main()
{
	int n;
	while (cin>>n&&n)
	{
		int cnt=0;
		int m;
		for(int i=1;i<=n;i++)
		{
			cin>>m;
			cnt^=m;
		}
		if(cnt)cout<<"yes";//先手胜
		else cout<<"no"; 
	}
	return 0;
}

第二个类型:最后取完者判输//反nim
统计一下所有数一下大于1的个数,并将所有数字异或一遍,若大于1的个数为0&&异或和为0||大于1的个数大于0&&异或和不为零,则先手胜,否则后手胜。

//hdu1907
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
    	int n;
        scanf("%d",&n);
        int f=0;
        int sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&x);
            if(x>1) f=1;
            sum^=x;
        }
        if((sum&&f)||(!sum&&!f))
            puts("John");
        else
            puts("Brother");
    }
    return 0;
}

第三个类型:nimk
限制最多取的个数,例如第i堆石子共有m个,最多取r个,先对m=m%(r+1);然后在进行异或求和。再根据异或和判断输赢。
例题:sdnuoj1253

//nimk博弈问题 
#include<cstdio>
#include<iostream>
#include<string.h>
using namespace std;
 
const int maxn = 10005;
int sg[maxn],eor[maxn];
int num,maxx,hy;

bool nimk(int n, int m)
{
	memset(eor, 0, sizeof(eor));
	maxx = -1;
	for(int i=1;i<=n;i++)
	{
		hy=sg[i];
		num=0;
		while(hy)
		{
			eor[num]+=hy&1;
			num++;
			hy>>=1; 
		}
		maxx=max(maxx,num);
	}
	for(int i=0;i<maxx;i++)
	{
		if(eor[i]%(m+1))
			return true;
	}
	return false;
}
int main()
{
	int T;
	cin >> T;
	int cas = 1;
	while(T--)
	{
		memset(sg, 0, sizeof(sg));
		int n, m;
		cin >> n >> m;
		
		for(int i = 1; i <= n; i++)
			cin >> sg[i];
			
		cout<<"Case #"<<cas<<": ";
		if(nimk(n, m))
			cout<<"Alice\n";//先手胜
		
		else
			cout<<"Bob\n";//后手胜
			
		cas++;
	}
	return 0;
}

第四种类型:先手的人想赢,第一步有多少种选择。

//hdu1850
#include <iostream>
#include <algorithm>
#include<string>
using namespace std;
int a[1000010];
int main()
{
	int n;
	while (cin>>n&&n)
	{
		int cnt=0;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
			cnt^=a[i];
		}
		int sum=0;
		for(int i=1;i<=n;i++)
			if((cnt^a[i])<a[i])sum++;
		cout<<sum<<'\n'; 
	}
	return 0;
}

第五种类型:限制最多取的堆数
问题:有两个绝顶聪明的人在玩取石子游戏,假设有n堆石子,每个人可以轮流在不超过k堆石子中拿走任意数量的石子(不能为0),无法拿石子的一方失败,问先手还是后手胜利。

当且仅当所有堆石子数在二进制下的各位上的1的数目都满足是k+1的倍数的时候是必败局面。//异或取模k+1=0?

借鉴
例题:
台阶(只考虑奇数台阶)
现在,有一个n级台阶的楼梯,每级台阶上都有若干个石子,其中第i级台阶上有ai个石子(i≥1)。

两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。

已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

#include<bits/stdc++.h>
using namespace std;

int main()
{  
    int n;cin>>n;
    int x = 0;
    for(int i=1,t;i<=n;i++){
        cin>>t;
        if(i&1) x^=t;//i&1----i%2==1
    }
    if(x) cout<<"Yes\n";
    else cout<<"No\n";
    return 0;
}

借鉴
四. 斐波那契博弈

有一堆物品,两人轮流取物品,先手最少取一个,至多无上限,但不能把物品取完,之后每次取的物品数不能超过上一次(即对手)取的物品数的二倍且至少为一件,取走最后一件物品的人获胜。

结论是:先手胜当且仅当n不是斐波那契数(n为物品总数)

#include <iostream>  
#include <stdio.h>  
using namespace std;  
const int N = 55;    
int f[N];   
void Init()  
{  
    f[0] = f[1] = 1;  
    for(int i=2;i<N;i++)  
        f[i] = f[i-1] + f[i-2];  
}    
int main()  
{  
    Init();  
    int n;  
    while(cin>>n)  
    {  
        if(n == 0) break;  
        bool flag = 0;  
        for(int i=0;i<N;i++)  
        {  
            if(f[i] == n)  
            {  
                flag = 1;  
                break;  
            }  
        }  
        if(flag) puts("Second win");  
        else     puts("First win");  
    }  
    return 0;  
} 

五.贝蒂定理(Betti theorem)//没整明白

设a、b是正无理数且 1/a +1/b =1。记P={ [na] | n为任意的正整数},Q={ [nb] | n 为任意的正整数},([x]'指的是取x的整数部分)则P与Q是Z+的一个划分,即P∩Q为空集且P∪Q为正整数集合Z+。

结论是:Z+=P∪Q。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值