前置知识
若一个游戏满足:
- 由两名玩家交替行动
- 在游戏进行的任意时刻,可以执行的合法行动与轮到哪位玩家无关
- 不能行动的玩家判负
则称该游戏为一个公平组合游戏。
尼姆游戏(NIM)属于公平组合游戏,但常见的棋类游戏,比如围棋就不是公平组合游戏,因为围棋交战双方分别只能落黑子和白子,胜负判定也比较负责,不满足条件2和3。
以下题目均属于公平组合游戏
题目一
解题思路
ai代表一种情况,a1a~2~a3^…an = 0代表先手必输情况;
a1a~2~a3^…an ≠ 0 代表先手必胜情况
(公式,背就完了)
代码实现
#include<iostream>using namespace std;int main()
{int res = 0;int n = 0;cin >> n;while (n -- ){int x;scanf("%d", &x);res ^= x;}if (res){cout <<"Yes";}else{cout << "No";}return 0;
}
题目二
解题思路
不严谨的思路:
移动偶数级台阶的石子是没有意义的,比如我把偶数级的石头移到下一级(即奇数级),对方又可以把其再移动到下一级(即偶数级)上,这样奇数级上的石子数量是保持不变的,因此这对我取胜是没有帮助的,移动了也等于没有移动。但如果我移动的是奇数级台阶上的石子,比如只有第一级有石子,我将第一级的石子移动到地面,我就赢了,所以真正影响胜负的是奇数级台阶的石子。
代码实现
#include<iostream>using namespace std;int main()
{int n;cin >> n;int res = 0;for (int i = 1; i <= n; i ++){int x;scanf("%d", &x);if (i % 2 != 0){res ^= x;}}if (res){cout << "Yes";}else{cout<< "No";}return 0;
}
题目三
解题思路
原题解链接:https://www.acwing.com/solution/content/23435/
代码实现
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>//任意map都行using namespace std;const int N=110,M=10010;
int n,m;
int f[M],s[N];//s存储的是可供选择的集合,f存储的是所有可能出现过的情况的sg值int sg(int x)
{if (f[x] != -1) return f[x];//因为取石子数目的集合是已经确定了的,所以每个数的sg值也都是确定的,如果存储过了,直接返回即可set<int> S;//set代表的是有序集合(注:因为在函数内部定义,所以下一次递归中的S不与本次相同)for (int i = 0; i < m; i ++ ){int sum = s[i];if (x >= sum) S.insert(sg(x-sum));//先延伸到终点的sg值后,再从后往前排查出所有数的sg值}for (int i = 0; ; i ++ )//循环完之后可以进行选出最小的没有出现的自然数的操作if (!S.count(i))return f[x] = i;
}int main()
{cin >> m;for (int i = 0; i < m; i ++ )cin >> s[i];cin >> n;memset(f,-1,sizeof(f));//初始化f均为-1,方便在sg函数中查看x是否被记录过int res = 0;for(int i = 0; i < n; i ++ ){int x;cin >> x;res ^= sg(x);//观察异或值的变化,基本原理与Nim游戏相同}if (res) printf("Yes");else printf("No");return 0;
}