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

来源:互联网 发布:windows无法识别u盘 编辑:程序博客网 时间:2024/06/16 09:59

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
212 19 2021 191 18
 

Sample Output
NOYES
 

Author
HIT
 

Source
2016 Multi-University Training Contest 1

#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|1typedef 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)*/

0 0