POJ 1704 Georgia and Bob

前置芝士

要解决这道题,我们先来了解一下 阶梯博弈

背景

\(n\) 个阶梯,地面表示 \(0\) 号阶梯,从下往上编号。每个阶梯都有若干石子,每人每次可以将一个阶梯上的任意个石子移到下一个阶梯。当面临所有石子都位于地面的人输。

解决方案

实际上这可以等价于奇数号阶梯的 \(\rm Nim\) 游戏。

如果对手移动偶数号阶梯的石子,那么我一定可以将它移动的石子移动到下一个偶数号阶梯,这就和奇数号阶梯无关了。而将奇数号阶梯的石子移动到偶数号阶梯,不就相当于取走了石子吗?

简要题意

\(\mathtt G\) 和小 \(\mathtt B\) 在玩游戏。给定一个很长的棋盘,每个格子只能放 \(1\) 个棋子,在棋盘上有 \(n\) 个棋子。

每个人每次必须要向左移动 \(1\) 个棋子,但不能移出棋盘。求先手是否必胜。

解法

先考虑 \(n\) 为偶数的情况:将棋子从左到右两两分为一组,若组内棋子之间空格个数异或和为 \(0\) 则必败;反之必胜。

为啥捏?类似上文的阶梯博弈,我们需要证明组外的空格没有什么用。显然,如果对手移动组内的左棋子,我们跟着移动组内的右棋子是合法的,而且这保证了一个重要的性质 —— 组内空格数不变!故证。

如果 \(n\) 为奇数,则将第一个单开(实际上是和坐标为 \(0\) 的棋子为一组),剩下还是从左到右两两分为一组。这还是为了保证 “组外空格无用”:左棋子无法移动;且除了第一个棋子自己动,其它的变化不会影响组内空格数。

代码

#include<cstdio>
#include<algorithm>
using namespace std;

int T, n, a[1002], ans;

int read() {
	int x = 0, f = 1; char s;
	while((s = getchar()) > '9' || s < '0') if(s == '-') f = -1;
	while(s >= '0' && s <= '9') {
		x = (x << 1) + (x << 3) + (s ^ 48);
		s = getchar();
	}
	return x * f;
}

int main() {
	T = read();
	while(T --) {
		ans = 0;                                                                                                                                                                                 
		n = read();
		for(int i = 1; i <= n; ++ i) a[i] = read();
		sort(a + 1, a + n + 1);
		for(int i = 1; i <= n; i += 2) ans ^= a[n - i + 1] - a[n - i] - 1;
		puts(ans ? "Georgia will win" : "Bob will win");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/12330234.html