【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游戏。

这种做法在简单博弈论中很常见。

posted @ 2022-03-16 17:22  zhangtingxi  阅读(153)  评论(0编辑  收藏  举报