本文搬运自本人初中博客园博客,若图片加载不出来,可到原文查看:https://www.cnblogs.com/zhangtingxi/p/16013639.html

题目链接

题目

小H和小Z正在玩一个取石子游戏。 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,每次取石子的个数有限制,谁不能取石子时就会输掉游戏。 小H先进行操作,他想问你他是否有必胜策略,如果有,第一步如何取石子。

思路

博弈论,考虑把题目变成Nim游戏。

[0,1000][0, 1000] 按可行操作变成一个有向图,然后处理出它们的SG函数。

然后,把原先的每堆石子通过SG函数转化为真正的石子,然后跑一遍Nim即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#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游戏。

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