巴什
两个人在一堆n个物品中取1~m个,拿光者胜出。
n = k * (m + 1) + b,若无余数b,后手可根据先手凑出m + 1,从而必胜。若有余数b,先手第一次拿b个则必胜。
void solve(){
cin>>n>>m;
if(n % (m + 1)) puts("First win");
else puts("Second win");
}
威佐夫
有两堆物品,两个人轮流从一堆物品中拿若干个或者在两堆中同时拿相同数量,取光者胜出。
先手必输情况:{0, 0}, {1, 2}, {3, 5}, {4, 7},{6, 10},{8, 13}
b[i] = a[i](此前序列中从未出现过的值中的最小值) + i
可证得以下公式
if(n < m) swap(n, m);
if(int((n - m) * (sqrt(5) + 1) / 2.0) == m) cout<<"Second win"<<endl;
else cout<<"First win"<<endl;
斐波那契
一堆n个物品,两人轮流取,每次至少取一个,每次取不能超过上次取得两倍,取完者胜。
判断n是否是斐波那契数,是则先手必败
a[0] = a[1] = 1;
for(int i = 2; i <= 45; ++i){
a[i] = a[i - 1] + a[i - 2];
}
for(int i = 2; i <= 45; ++i){
if(a[i] == n){
puts("Second win");
return;
}
}
puts("First win");
nim
n堆物品,两人轮流取,每次至少在一堆里取一个,取完者胜。
对每一堆物品进行异或处理,若结果为0先手必败
int tot = 0;
for(int i = 1; i <= n; ++i) cin>>a[i],tot^=a[i];
if(tot == 0){
puts("Second win");
}
else puts("First win");
若已知先手获胜,问其先手有几种取法能获胜,则每次异或一堆物品的数量,再比较结果与当前堆的数量即可,即让后手的人开始取的时候异或结果为0。
int ans = 0;
for(int i = 1; i <= n; ++i){
int tmp = tot ^ a[i];
if(tmp < a[i]) ans++;;
}
反nim
在nim 的条件下,谁取完谁输。
计算所有堆中充裕堆(数量 > 1)的数量,以及最终的异或结果res。
若 res == 0 && cnt == 0 || res != 0 && cnt != 0,先手必胜,否则后手必胜。
int res = 0, cnt = 0;
while(n--){
cin>>m;
res ^= m;
if(m > 1) cnt++;
}
if(res){
if(cnt) puts("First win");
else puts("Second win");
}
if(!res){
if(!cnt) puts("First win");
else puts("Second win");
}