1. 程式人生 > >博弈論(取石子專題)

博弈論(取石子專題)

有一堆石子共有N個。A B兩個人輪流拿,A先拿。每次最少拿1顆,最多拿K顆,拿到最後1顆石子的人獲勝。假設A B都非常聰明,拿石子的過程中不會出現失誤。給出N和K,問最後誰能贏得比賽。

必勝策略:令 n = (k + 1) * r + s ; A第一次取s個,讓B面對k+1倍數的局面,如果B取m個則A取k+1 - m個。

#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	int t;
	scanf("%d", &t);
	while( t-- )
	{
		int n, k;
		scanf("%d%d", &n, &k);
		if( n <= k )
			printf("A\n");
		else
		{
			int tmp = n % (k + 1);
			if( tmp == 0 )
				printf("B\n");
			else
				printf("A\n");
		}
	}

	return 0;
}

有一堆石子共有N個。A B兩個人輪流拿,A先拿。每次只能拿1,3,4顆,拿到最後1顆石子的人獲勝。假設A B都非常聰明,拿石子的過程中不會出現失誤。給出N,問最後誰能贏得比賽。

分析:連續找一些書測試,可以發現當 n % 7 == 0 || == 2 時B勝,其餘情況A勝

#include <cstdio>
using namespace std;

int main()
{
	int t, n;
	scanf("%d", &t);
	while( t-- )
	{
		scanf("%d", &n);
		int tmp = n % 7;
		if( tmp == 0 || tmp == 2 )
			printf("B\n");
		else
			printf("A\n");
	}

	return 0;
}

有N堆石子。A B兩個人輪流拿,A先拿。每次只能從一堆中取若干個,可將一堆全取走,但不可不取,拿到最後1顆石子的人獲勝。假設A B都非常聰明,拿石子的過程中不會出現失誤。給出N及每堆石子的數量,問最後誰能贏得比賽。

例如:3堆石子,每堆1顆。A拿1顆,B拿1顆,此時還剩1堆,所以A可以拿到最後1顆石子。

分析:尼姆博弈,對於一個Nim遊戲的局面(a1,a2,...,an),它是P-position當且僅當a1^a2^...^an=0,其中^表示異或(xor)運算.

#include <cstdio>
using namespace std;

int main()
{
	int n;
	while( ~scanf("%d", &n) )
	{
		int ans = 0;
		int num;
		for( int i=0; i<n; i++ )
		{
			scanf("%d", &num);
			if( i == 0 )
				ans = num;
			else
				ans ^= num;
		}
		if( ans != 0 )
			printf("A\n");
		else
			printf("B\n");
	}

	return 0;
}

有2堆石子。A B兩個人輪流拿,A先拿。每次可以從一堆中取任意個或從2堆中取相同數量的石子,但不可不取。拿到最後1顆石子的人獲勝。假設A B都非常聰明,拿石子的過程中不會出現失誤。給出2堆石子的數量,問最後誰能贏得比賽。

例如:2堆石子分別為3顆和5顆。那麼不論A怎樣拿,B都有對應的方法拿到最後1顆。

分析:威佐夫博弈

兩個人如果都採用正確操作,那麼面對非奇異局勢,先拿者必勝;反之,則後拿者取勝. 局勢(N,M)(N<M)滿足

N==(int)((1.0+sqrt(5.0))/2.0*(M-N))時,該局勢為奇異局勢,後拿者必勝.

#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;

int main()
{
	int t;
	scanf("%d", &t);
	while( t-- )
	{
		int a, b;    // n m
		scanf("%d%d", &a, &b);
		if( a > b )
			swap(a, b);
		int tmp = (int) ((1.0 + sqrt(5.0)) / 2.0 * (b - a));	// 奇異局勢
		if( a == tmp )
			printf("B\n");
		else
			printf("A\n");
	}

	return 0;
}