本文搬运自本人初中博客园博客,若图片加载不出来,可到原文查看:https://www.cnblogs.com/zhangtingxi/p/16013639.html
题目
小H和小Z正在玩一个取石子游戏。 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,每次取石子的个数有限制,谁不能取石子时就会输掉游戏。 小H先进行操作,他想问你他是否有必胜策略,如果有,第一步如何取石子。
思路
博弈论,考虑把题目变成Nim游戏。
把 [ 0 , 1000 ] [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游戏。
这种做法在简单博弈论中很常见。