1. 程式人生 > 其它 >【BZOJ 1874:[BeiJing2009 WinterCamp]取石子游戲 】題解

【BZOJ 1874:[BeiJing2009 WinterCamp]取石子游戲 】題解

題目連結

題目

小H和小Z正在玩一個取石子游戲。 取石子游戲的規則是這樣的,每個人每次可以從一堆石子中取出若干個石子,每次取石子的個數有限制,誰不能取石子時就會輸掉遊戲。 小H先進行操作,他想問你他是否有必勝策略,如果有,第一步如何取石子。

思路

博弈論,考慮把題目變成Nim遊戲。

\([0, 1000]\) 按可行操作變成一個有向圖,然後處理出它們的SG函式。

然後,把原先的每堆石子通過SG函式轉化為真正的石子,然後跑一遍Nim即可。

Code

#include<bits/stdc++.h>
using namespace std;
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define N 1010
int n, m, i, j, k, T;
int a[N], b[N], c[N], t[N], x[N], ans; 

signed main()
{
	n=read(); 
	for(i=1; i<=n; ++i) a[i]=read(); 
	m=read(); 
	for(i=1; i<=m; ++i) b[i]=read(); 
	for(i=1; i<=1000; ++i)
	{
		for(j=1; j<=m && b[j]<=i; ++j) 
			t[c[i-b[j]]]=i; 
		for(j=0; j<=1000; ++j)
			if(t[j]!=i) break; 
		c[i]=j; 
	}
	for(i=1; i<=n; ++i) x[i]=c[a[i]]; 
	for(i=1; i<=n; ++i) ans^=x[i]; 
	printf(ans ? "YES\n" : "NO\n"); 
	if(ans)
	{
		for(i=1; i<=n; ++i)
			for(j=1; j<=m && b[j]<=a[i]; ++j)
				if(c[a[i]-b[j]]==(ans^x[i]))	
					return printf("%d %d", i, b[j]), 0; 	
	}
	return 0;
}

總結

對於這類常見的非經典Nim遊戲問題,可以通過有向圖預處理SG函式,轉化為Nim遊戲。

這種做法在簡單博弈論中很常見。