Chess sg值 Nim游戏

Alice and Bob are playing a special chess game on an n × 20 chessboard. There are several chesses on the chessboard. They can move one chess in one turn. If there are no other chesses on the right adjacent block of the moved chess, move the chess to its right adjacent block. Otherwise, skip over these chesses and move to the right adjacent block of them. Two chesses can’t be placed at one block and no chess can be placed out of the chessboard. When someone can’t move any chess during his/her turn, he/she will lose the game. Alice always take the first turn. Both Alice and Bob will play the game with the best strategy. Alice wants to know if she can win the game.

Input

Multiple test cases.

The first line contains an integer T(T≤100)

, indicates the number of test cases.

For each test case, the first line contains a single integer n(n≤1000), the number of lines of chessboard.

Then n lines, the first integer of ith line is m(m≤20), indicates the number of chesses on the ith line of the chessboard. Then m integers pj(1≤pj≤20)

followed, the position of each chess.

Output

For each test case, output one line of “YES” if Alice can win the game, “NO” otherwise.

Sample Input

2
1
2 19 20
2
1 19
1 18

Sample Output

NO
YES

题意:Alice和Bob在下象棋,棋盘有n行,每行20个格子,有一些格子上面有一些棋子,每次可以把一个棋子移到它右面的最左面的格子中去。Alice先手,谁先无法移动棋子谁就输了。问Alice是否能赢?

分析:根据题意,可以把游戏分为n个互不相关的子游戏。。

每个子游戏都可以根据当前状态计算出一个sg值,从而把这个问题转化成一个 Nim 游戏 。

在开始的时候先预处理出所有状态的sg值,最后把它们(每一行的状态的sg值)异或起来,和为零先手就会输,否则先手就会赢。sg值的计算可以用记忆化搜索的办法。

记忆化搜索的时候,找到后继状态,然后计算它的 sg值,用一个max数组来表示已经 sg值 是否已经被用过。如何寻找后继状态?很简单,从右起找到一个为零的位置就用pos变量记录下来,等下一次找到一个1的时候把它俩互换一下就可以了。

#include <iostream>
#include <set>
#include <cstdio>
using namespace std;
const int N=1<<20;

int sg[N];

int grundy(int x){                      
	if (sg[x]!=-1) return sg[x];    //记忆化搜索

	int max[50]={0};          //定义一个数组顺便初始化为零

	int pos=-1;

	for (int i = 0; i < 20; i++)
	{
		if ((x & (1<<i)) == 0)  pos = i;         //pos记录为零的位置
		else if ( pos != -1 ) max[grundy( (x ^ (1<<i)) | ( 1<<pos ))] = 1;
		//后继状态的grundy值对应的max[]记为1
		//这一位为1则放到pos记录的位置上 。 ^异或:按位取反  | 或:把零变为1
	}

	for (int i=0; i<20; i++)
		if (!max[i]) 
			return sg[x]=i;
}

void init()        //预处理所有状态的sg值
{
	for (int i = 0; i < N; ++i)
	{
		sg[i]=grundy(i);
	}
}

int main()
{
	int t,n,num,ans,x,m;

	for (int i = 0; i < N; ++i)
	{
		sg[i]=-1;
	}

	init();

	scanf("%d" , &t);
	while (t--)
	{
		scanf("%d", &n);

		ans=0;
		for (int i = 0; i < n; i++)
		{
			scanf("%d", &m);	
			num=0;
			for (int j = 0; j < m; ++j)
			{
				scanf("%d", &x);
				x=20-x;
				num|=(1<<x);
			}
			ans^=sg[num];
		}

		if (!ans) cout<<"NO\n";
		else cout<<"YES\n";
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41703679/article/details/81483301