您的位置:首页 > 大数据 > 人工智能

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

2016-07-25 09:46 417 查看

Chess

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 1369    Accepted Submission(s): 597


[align=left]Problem Description[/align]
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.
 

[align=left]Input[/align]
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.

 

[align=left]Output[/align]
For each test case, output one line of “YES” if Alice can win the game, “NO” otherwise.
 

[align=left]Sample Input[/align]

2
1
2 19 20
2
1 19
1 18

 

[align=left]Sample Output[/align]

NO
YES

 

[align=left]Author[/align]
HIT
 

[align=left]Source[/align]
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|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)

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息