基础博弈巴什,威佐夫,尼姆——例题+板子

巴什博弈

  • 定义:只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜
  • 结论:如果n%(m+1)==0 后手必胜,否则先手必胜
  • 例题hdu
  • 板子
#include<bits/stdc++.h>

using namespace std;

int main()
{
    
    int t;
    cin>>t;
    while(t--)
    {
        int n,m;
        cin>>n>>m;
        if(n%(m+1)==0) puts("second");
        else puts("first");
    }
    return 0;
 } 

威佐夫博弈

参考博客

  • 定义:
    1有两堆各若干个物品,两个人轮流从任一堆取至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜
    2奇异局势:先手必输(后手必赢)
  • 结论:两堆物品n,m个(n<=m), 如果第一个数等于两数差值*黄金比例再向下取整,那么是一个奇异局势
  • 例题:poj
  • 板子:
#include<iostream>
#include<cmath>
#include<cstdio>
#define endl '\n'
using namespace std;
const int M = 1000;
int a[M];
const double lorry =(sqrt(5.0)+1.0)/2;
//奇异局势:第一个数是两数差值*黄金比列向下取整 
int main()
{
	
	int m,n;
	while(cin>>n>>m)
	{
		if(n>m) swap(n,m);
		int d = m-n;
		if(n==(int)(d*lorry)) printf("0\n");
		else printf("1\n");
	}
	return 0;
 } 

尼姆博弈:

  • 定义:有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

  • 结论:各堆石子数量异或和为0则后手胜利,否则先手胜利

  • 例题:hdu

  • 这道题,尼姆博弈稍微变形了一下题解参考大佬

  • 本人理解:
    尼姆博弈:异或和为0则先手必输,后手必赢,那么这道题就是求先手有的多少种方法让异或和为0,如果先手让异或和为0了后在开始,那么相当于后手变成先手了 。
    设sum为异或和,a为其中一个数,b为除a以外的异或和,sum=a^ b 则b=sum^a,要让sum=0,则要让a=b,如果a>b=sum ^a,那么则可以

  • 代码

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
using namespace std;
const int N = 168;
int a[N];
int main()
{
	//尼姆博弈:
	//异或和为0则先手必输,后手必赢
	//那么这道题就是求先手有的多少种方法让异或和为0
	//如果先手让异或和为0了后在开始,那么相当于后手变成先手了 
	IOS;
	int n;
	while(cin>>n&&n)
	{
		
		int sum = 0;
		for(int i=0; i<n; i++)
		{
			cin>>a[i];
			sum^=a[i];
		}
		int ans = 0;
		for(int i=0; i<n; i++)
		{
			if(a[i]>(sum^a[i])) 
			{
				ans++;
			}
		} 
		cout<<ans<<endl;
	}
	return 0;
 } 

反尼姆博弈

  • 定义:有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得
  • 结论:先手胜利条件:
    1各堆石子个数不都为1, 异或和不为0
    2各堆石子个数都为1,异或和为0
    否则后手胜利
  • 例题hdu
  • 板子
#include<iostream>
#include<cmath>
#include<cstdio>
#define endl '\n'
using namespace std;
const int N = 520;
int a[N];
//反尼姆博弈:最后取石子得人输 
int main()
{
	
	int t;
	cin>>t;
	while(t--)
	{
		int n;
		scanf("%d",&n);
		int sum = 0;
		int flag = 0;
		for(int i=0; i<n; i++)
		{
			scanf("%d",&a[i]);
			sum=sum^a[i];
			if(a[i]>1) flag =1;
		}
		//反尼姆博弈:先手获胜得条件;
		//1各堆石子个数不都为1, 异或和不为0
		//2各堆石子个数都为1,异或和为0
		//否则后手胜利 
		if(flag&&sum) puts("John");
		else if(!flag&&sum==0) puts("John");
		else puts("Brother");
	}
	return 0;
 } 
posted @ 2022-08-28 08:44  翔村亲亲鸟  阅读(113)  评论(0编辑  收藏  举报