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

 

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