邂逅明下(巴什)
题目大意
n 枚硬币, 每次至少取p枚,最多q枚,如果剩下少于p枚就要一次取完。求先取的人是否有必胜策略。
分析
【小编当时遇题的思考】自然是从n和两数的关系入手, n <= p时先手必输, p < n <= 2p时,先手可赢。但当 n > 2p时,我们就得讨论q的大小关系,脑子一团乱麻,直接搜答案了(对自己的菜很是无语。。倒也不为难自己)
【合理解释】
感觉巴什博弈就是两者取的个数拼凑成一个数(一般固定),剩下一个不得不取的数就是必胜必败的情况
我们不妨换个比较的思路, n 和 (p + q)的关系。
- n = (p + q) * r, 先手第一次取q个, 后手每取k个,先手变取(p + q - k)个, 这两个均属于 [p, q]的范围。最后必胜 p个留给后手,先手必胜
- 若 n = (p + q) * r + left 个,
(1) 1 < left <= p ,先手必败。先手每取k个,后手均取(q + p - k)个,最后留 left给先手,先手必败
(2) left 在[p + 1, p + q - 1] 之间, 则先手可以拿走 t 个(拿的数量看 left 的大小,不能>=left, 保证最后剩的为[1, p]个), 以后后手每取k个,先手便取(p + q - k) 个, 最后剩 [1, p]个是后手要拿的,先手胜。
另一种思考方式可能更好:
此题为巴什博弈得变形,n % (p + q)的值与p比较, 若 = 0 或 > p, 则先手必胜(因为他可以不是最后拿的那个), 若 0<值<= p 则先手必输。
#include <algorithm>
#include <iostream>
using namespace std;
int main(void){
int n, p, q;
while(scanf("%d%d%d", &n, &p, &q) != EOF){
if(n % (p + q) > p || n % (p + q) == 0) puts("WIN");
else puts("LOST");
}
return 0;
}
Nim or not Nim? (尼姆博奕变形)
题目大意
尼姆博弈游戏在原有的游戏规则上附加一条——玩家除了取石子操作外,还可以将一堆石子分成两份,这也算一次操作;
思路
求出现的每堆石子的SG值再进行异或运算;我们重要的是求出每堆石子的SG值。打表也需要一定的基本思想
SG[x]=mex(SG[y]). y是x的后续(通俗的讲,y是x可以一步到达的状态));mex()函数是求集合中未出现的最小非负整数如:mex(0,2,3)=1; 更多内容可参考小编的这篇博客
博客链接
SG(0) = 0;
SG(1) = mex(SG(0)) = 1;
SG(2) = mex(SG(0), SG(1), SG(1) ^ SG(1)) = 2;
2 可以分成两堆 1 , 1,
【一个定理:每一堆都可变成多个局面,每个局面都有两堆b1, b2, sg(b1, b2) = sg(b1) ^ sg(b2)】
SG(3) = mex(SG(0), SG(1), SG(2), SG(1) ^ SG(2)) = mex(0, 1, 2, 3) = 4;
SG(4) = mex(SG(0), SG(1), SG(2), SG(3), SG(1) ^ SG(3) , SG(2) ^ SG(2)) = mex(0, 1, 2, 4, 2, 0) = 3;
SG(5) = mex(SG(0), SG(1), SG(2), SG(3), SG(4), SG(1) ^ SG(4), SG(2) ^ SG(3)) = mex(0, 1, 2, 4, 3, 2, 1) = 5
SG(6) = mex(SG(0), SG(1), SG(2), SG(3), SG(4), SG(5), SG(1) ^ SG(5), SG(2) ^ SG(4), SG(3) ^ SG(3)) = mex(0, 1, 2, 4, 3, 5, 4, 1, 0) = 6
SG(7) = mex(SG(0), SG(1), SG(2), SG(3), SG(4), SG(5), SG(6), SG(1) ^ SG(6), SG(2) ^ SG(5), SG(3) ^ SG(4)) = 8
SG(8) = mex(SG(0), SG(1), SG(2), SG(3), SG(4), SG(5), SG(6), SG(7), SG(1) ^ SG(7), SG(2) ^ SG(6), SG(3) ^ SG(5), SG(4) ^ SG(4)) = 7
…
这样觉得偶然的话,那就再打几个。
规律:
n % 4 = 0时, SG(n) = n - 1;
n % 4 = 3: SG(n) = n + 1;
其他: SG(n) = n。
代码
#include <algorithm>
#include <iostream>
using namespace std;
int n, x;
int main(void){
int t;
cin >> t;
while(t --){
scanf("%d", &n);
int res = 0;
int t = 0;
for(int i = 0; i < n; i ++){
scanf("%d", &x);
if(x % 4 == 0) res ^= (x - 1);
else if(x % 4 == 3) res ^= (x + 1);
else res ^= x;
}
if(res == 0) puts("Bob");
else puts("Alice");
}
return 0;
}