【HDU5724 2016 Multi-University Training Contest 1B】【博弈 SG函数】Chess 棋子跳棋向右移 先后手胜负博弈

本文介绍了一种在n*20棋盘上进行的游戏策略,两位玩家轮流移动棋子,探讨了游戏的胜负条件及SG函数的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Chess

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1369    Accepted Submission(s): 597


Problem Description
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(T100) , indicates the number of test cases.

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

Then   n  lines, the first integer of ith line is   m(m20) , indicates the number of chesses on the ith line of the chessboard. Then m integers   pj(1pj20) 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
 

Author
HIT
 

Source

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, m;
int b[24];
int sg[1 << 20];
int no[24];
void init()
{
	for (int i = 0; i < 20; ++i)b[i] = 1 << i;
	int top = 1 << 20;
	for (int i = top - 1; ~i; --i)
	{
		MS(no, 1);
		int blk = -1;
		for (int j = 19; ~j; --j)
		{
			if (i >> j & 1)
			{
				if (~blk)
				{
					int nxt = i ^ b[j] ^ b[blk];
					no[sg[nxt]] = 0;
				}
			}
			else blk = j;
		}
		while (!no[sg[i]])++sg[i];
	}
}
int main()
{
	init();
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d", &n);
		int SG = 0;
		for (int i = 1; i <= n; ++i)
		{
			int g; scanf("%d", &g);
			int sta = 0;
			while (g--)
			{
				int x; scanf("%d", &x); --x;
				sta |= b[x];
			}
			SG ^= sg[sta];
		}
		puts(SG ? "YES" : "NO");
	}
	return 0;
}
/*
【题意】
n*20的棋盘
每行有若干个棋子。
A和B轮流做游戏。
每次可以任选一行,把该行的某个棋子按照一定的规则移动。
最后谁不能移动就输了。
问先手胜负情况。

移动规则是这样的——
如果选定棋子右侧为空,则直接移过去。
否则把该棋子移动至右侧非空的第一个位置。

【类型】
博弈 SG函数

【分析】
这题对于每行,显然都可以认定为一个独立的子游戏。
行状态最多只有2^20,我们对其做个SG函数预处理。
然后把所有行的SG值异或起来,非零则先手必胜。

问题是——如何求SG值呢?
我们发现——
1,棋子只会往右移动,如果我们把b[i]定义为1<<i的话,右移操作只会使得状态编号越变越大。
	——于是我们按照状态数由大到小的顺序处理SG函数
2,每个状态的后继状态最多只有20个,这意味着
	——我们可以快速算出SG函数,且SG函数的值限定在[0,20]之间

【时间复杂度&&优化】
O(1 << 20 * 20)

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值