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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值