poj1704

题意:从左到右n个格子,某些有石子,每格永远只能放一个石子,每次可以将某个石子向左移若干格,但不能跨越任何石子。两人轮流操作,谁无路可走谁输。

分析:我们把棋子按位置升序排列后,从后往前把他们两两绑定成一对。如果总个数是奇数,就把最前面一个和边界(位置为0)绑定。 在同一对棋子中,如果对手移动前一个,你总能对后一个移动相同的步数,所以一对棋子的前一个和前一对棋子的后一个之间有多少个空位置对最终的结果是没有影响的。于是我们只需要考虑同一对的两个棋子之间有多少空位。我们把每一对两颗棋子的距离(空位数)视作一堆石子,在对手移动每对两颗棋子中靠右的那一颗时,移动几位就相当于取几个石子,与取石子游戏对应上了,各堆的石子取尽,就相当再也不能移动棋子了。
我们可能还会考虑一种情况,就是某个玩家故意破坏,使得问题无法转换为取石子,例如前一个人将某对中的前者左移,而当前玩家不将这对中的另一移动,则会导致本堆石子增多了,不符合nim。但是这种情况是不会出现的。因为赢家只要按照取石子进行即可获胜,而输家无法主动脱离取石子状态。如果输家想要让某堆石子增多,那么赢家只需要让该堆减少回原状,这样输家又要面临跟上一回合同样的局面。

View Code
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

#define maxn 1005

int n;
int f[maxn];

void input()
{
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &f[i]);
if (n & 1)
f[n++] = 0;
}

void work()
{
int ans = 0;
for (int i = n - 1; i > 0; i -= 2)
ans ^= f[i] - f[i - 1] - 1;
if (ans)
printf("Georgia will win\n");
else
printf("Bob will win\n");
}

int main()
{
//freopen("t.txt", "r", stdin);
int t;
scanf("%d", &t);
while (t--)
{
input();
sort(f, f + n);
work();
}
return 0;
}
posted @ 2011-09-29 16:39  金海峰  阅读(1296)  评论(0编辑  收藏  举报