編程之美1.12——“拈”遊戲分析

問題:

有N塊石頭和兩個玩家A和B,玩家A先將石頭隨機分成若干堆,然後按照BABA...的順序不斷輪流取石頭,能將剩下的石頭一次取光的玩家獲勝,每次取石頭時,每個玩家只能從若干堆石頭中任選一堆,取這一堆石頭中任意數目(大於0)個石頭。

請問:

玩家A要怎樣分配和取石頭才能保證自己有把握取勝?


如果石頭的個數N爲偶數,A只要將其分爲相同的兩份,就一定能取勝。

初始:XOR(M1, M1) == 0

玩家B:XOR(M1, M2) != 0  (其中一堆的個數減少到M2)

玩家A:XOR(M2, M2) == 0  (玩家A將另一堆的個數也減少到M2)

結果:XOR(M2, M2) == 0  (直到結束狀態(0, 0))


如果石頭的個數N爲奇數,B有必勝的方法。

初始:XOR(M1, M2, ... , Mn) != 0

玩家B:XOR(M1, ... , Mi', ... , Mn) == 0 (其中一堆Mi的個數減少到Mi')

玩家A:XOR(M1, ... , Mj', ... , Mn) != 0 

玩家B:XOR(M1, ... , Mi', ... , Mn) == 0 (其中一堆Mi的個數減少到Mi')

結果:XOR(M1, ... , Mj' , ... , Mn) == 0 (直到結束狀態(0,0))


這裏就有個問題:已知XOR(M1, M2, ... , Mn) != 0,玩家B該改變那個Mi以使得XOR(M1, ... , Mi', ... , Mn) == 0呢?

對於這個問題的答案,書中並未準確的結論。


經過本人的分析,所得到的結論如下:

設k=XOR(M1, M2, ... , Mn),已知k!=0,取一個數Mi,其二進制表達中在k的最高二進制位上的數爲1,且這個

數Mi肯定存在(k的這個最高位在異或運算中肯定來自某一個Mi)。在程序中滿足(Mi&k) > (k>>1)條件的數即爲Mi。


簡單證明:即假設k的二進制表達是1xx,那麼Mi的二進制表達是x...x1xx,這樣玩家B將該Mi改成Xi'=XOR(Mi, k)後,

Mi'的二進制表達是x...x0yy,肯定小於Mi,並且有XOR(M1, ... , Mi', ... , Mn) == 0。


下面的程序模擬了石頭個數N爲奇數的情況,其中玩家B用我 I 表示,玩家A用She表示。

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int A[10];

int main()
{
	int n = 25;	// 石頭總數(可變)
	int m = 5; 	// 劃分的堆數(可變)
	int i, k, s;
	int sum=0;
	srand(time(0));
	// 模擬m堆的石頭個數,A[i]表示第i堆石頭的個數
	for (i=1; i<m; i++)
	{
		A[i] = rand()%(5*i-sum);
		sum += A[i];
	}	A[m] = n - sum;
	while (true)
	{	
		// 輸出每個堆的石頭個數
		for (i=1; i<=m; i++)
			cout << A[i] << " ";
		cout << endl;

		int xor = 0;
		for (i=1; i<=m; i++)
			xor = xor^A[i];
		for (i=1; i<=m; i++)
			// 二進制表達中在k的最高二進制位上的數爲1						
			if ((A[i]&xor) > (xor>>1))  break;		
		printf("I: get %d stones from %d heap\n", A[i]-(A[i]^xor), i);		
		// 將Mi改爲Mi'
		A[i] = A[i]^xor;
		// 對方隨機取數據		
		for (i=1; i<=m && A[i]==0; i++);
		if (i>m) break;
		do
		{
			k = rand()%m + 1;
		}while (A[k] == 0);
		s = rand()%A[k] + 1;
		printf("She: get %d stones from %d heap\n", s, k);
		A[k] = A[k] - s;
	}
	return 0;
}


 

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