HDU - 1850 Being a Good Boy in Spring Festival (Nim博弈)+常见博弈模型

一年在外 父母时刻牵挂
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧

陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐

如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 ACM课上学的呢~

下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
——“先手的人如果想赢,第一步有几种选择呢?”

Input

输入数据包含多个测试用例,每个测试用例占2行,首先一行包含一个整数M(1<M<=100),表示扑克牌的堆数,紧接着一行包含M个整数Ni(1<=Ni<=1000000,i=1…M),分别表示M堆扑克的数量。M为0则表示输入数据的结束。

Output

如果先手的人能赢,请输出他第一步可行的方案数,否则请输出0,每个实例的输出占一行。

Sample Input

3
5 7 9
0

Sample Output

1

思路:一个基础的Nim博弈,首先我们要知道一个东西就是a1^a2^a3....an=0的话那么先手必败

若a1^a2^...^an!=0,一定存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。若a1^a2^...^an=k,则一定存在某个ai,它的二进制 表示在k的最高位上是1(否则k的最高位那个1是怎么得到的)。这时ai^k<ai一定成立。则我们可以将ai改变成ai'=ai^k,那么最后^起来还是为0

#include<iostream>
#include<cstdio>
using namespace std;
const int N=105;
int a[N];
int main(){
	int n,temp=0,cnt;
	while(cin>>n&&n){
		cnt=0;
		temp=0;
		for(int i=0;i<n;i++){
			cin>>a[i];
			temp^=a[i];
		}
		for(int i=0;i<n;i++){
			int t=a[i]^temp;
			if(a[i]>t) cnt++;//该堆石子在二进制的最高位上
		}
		cout<<cnt<<endl;
	}
	return 0;
} 

 

常见博弈总结:

1.Nim博弈

n堆石子,两人轮流取,只能在一堆中取,规定每次至少取一个,最多取一堆,最后取完者获胜

用0与每个数异或,如最后结果为0,则后手胜

  • 设一数组a[m],令sum=0
  • 循环与数组每一个数据异或(sum^=a[i])
  • sum最后等于0则后手胜
0异或各堆物品,结果为零————后手胜

//a[m]每堆物品的数量  sum=0
int Nimm_Game(int m)
{
    for(int i=0;i<m;i++)
	sum = sum ^ a[i];
    if(sum == 0)
	return 0;
    return 1;
}

 

2.巴什博弈

只有一堆n个物品,两个人A,B轮流从这堆物品中取物,规定每次至少取一个,最多取m个,取最后一个物品的人得胜

分析:

  • 当 n = m + 1 时,第一个人不可能获胜;
  • 当 n = k*(m + 1) + r 时,先取者拿走 r 个,那么后者再拿(1~m)个 , 此时 n =(k-1)*(m+1)+s先取者再拿走s 个 最后总能造成 剩下n=m+1 的局面
  • 若n=k*(m+1) 那么先取者必输
int Bash_Game(int n,int m)
{
    if (n%(m+1)==0) //后手胜
	return 0;
    return 1;
}

 

3.威佐夫博弈

    两堆各有若干个物品,两人轮流从某一堆中任意取或者同时从两堆中取同样多的物品最后取光着胜

  • 两堆石子的状态为 [ak,bk] (满足ak<=bk)
  • 当 ak=(k*(√5+1)/2), bk=ak+k 时满足奇异局势,那么则先手输,反之则先手赢
若(大-小)*(1+sqrt(5))/2==小————后手胜

t=(max(n,m)-min(n,m))*(1+sqrt(5))/2;
if(t==min(n,m)) 后手胜
else 先手胜

int Wythoff_Game(int a,int b)
{
    double x=(1+sqrt(5))/2;
    int n=b-a;
    if(a==(int)(x*n))
        return 1;
    return 0;
}

 

4.环形博弈

n个石子围成一个环,每次取一个或者取相邻的两个取完最后石子的人

if 石子数<=2 先手胜
else 先手输(消灭不对称因素后对称取石子)

 

5.斐波那契博弈

一堆石子n个

  • 先手不能在第一次把所有的石子取完,至少取一颗
  • 之后每次可以取得石子数至少为1,至多为对手取得石子数的2倍
  • 约定取走最后一个石子的人为赢家

结论:若n为斐波那契数列,则后手胜

 

 参考博客:https://blog.csdn.net/qq_40907279/article/details/83718880

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章