HDU 5724 Chess(国际象棋)

HDU 5724 Chess国际象棋

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


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.


Alice和Bob正在一个n × 20的棋盘上玩自定国际象棋。

棋盘上有若干子。每方在本回合只能行一子。如果被移动的子的右邻格为空,则移动之。否则跳过挡路的子,直接移动到他们的右边。一格只能落一子,并且棋子不能移出界。若某人在己方回合无法移动任何子,则输。

Alice总是执先手。Alice与Bob都会使用最佳策略。Alice希望得知自己是否能赢得这场游戏。


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.


多组测试用例。

第一行有一个整数T(T≤100),表示测试用例的数量。

对于每个测试用例,第一行有一个整数n(n≤1000),表示棋子的行数。

随后n行,每行第一个数为m(m≤20),表示此行棋子的数量。接着有m个整数pj(1≤pj≤20),表示各个棋子的位置。


Output


输出


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


对于每个测试用例,如果Alice能赢则输出“YES”,否则输出“NO”。


Sample Input - 输入样例


Sample Output - 输出样例


2
1
2 19 20
2
1 19
1 18


NO
YES

【题解】

SG定理,百度上解释看着不明觉厉,自己琢磨实现拙劣难成,最后再结合别人的代码似乎自行理解了一下,然而回头看百度上的解释依然看不懂。

总结自百度:

这种游戏必定能分出输赢,不是我方输就是对方赢。每种状态都当初是我发先手,最后必输的状态是确定的。即SG[x] = 0,必输。

如果我方的起手状态是SG[x] = 0的上一个状态,则必定 SG[y] = 1,必胜。

输赢某种意义上是不断转化的。

由此,必输状态的下一个状态是必胜状态。必胜状态的下一个状态如果不是必输状态,则为若干状态相互组成,这里我个人称作吊打状态(我告诉你,我有X种方式来解决些问题)。

总结自代码以及个人作死:

对于单场(一行)游戏来说:

SG = 0,无力回天。

SG = 1,高手躺赢。

SG > 1,花式吊打。

因为这道题涉及到多场游戏,所以我们需要区分种类不同的花式吊打,比如花式吊打对手的时候可能是:无情吊打(SG = 2),残忍虐杀(花式吊打SG = 3),笑而不语(SG = 4)…………等等。

然后我们从0(必输)开始看看是怎么确定出上一个状态的。

先祖状态 SG = 神马都没有,当前状态 0

先祖状态SG = {0},当前状态 1

(问题来了,如何才能吊打对手?这局我想输就输,想赢就赢。)

先祖状态SG = {0, 1},当前状态 2

(似乎掌握了所有前置先祖状态才能推导出下一个状态,不能解决前面的所有状态,怎么能叫吊打?)

因此当前SG[now] = 先祖SG{a,b,c……}中从0开始的最长连续长度。

先祖SG为空,当前SG为0。

(其中先祖SG无重复,因为重复出现相当于某种一样上的等价先祖,取谁有一样)

个人总结SG定理:连续花式吊打数。

关于总场次的胜负判定:

对于各个场次,我发先手状态下若为:必胜 + 必胜

最终结果则为 必负。

因此:

必胜 + 必胜 = 必负。

必负 + 必负 = 必负。

必胜 + 必负 = 必胜。

此处直接转为疑惑运算(你要判断奇偶我也不拦你……):

1 ^ 1 = 0;

0 ^ 0 = 0;

1 ^ 0 = 0;

所以真正能决定某种状态(SG)变化的只有相同的状态(SG),结果的运算也就是见招拆招,所以就是不断地异或(真的,你要算奇偶我不会拦你的)

最终结果:SG[a1] ^ SG[a2] ^ ………… ^ SG[an]

对应单场游戏的结果:

0,默哀

1,躺赢

大于1,吊打

【代码 C++】

 1 #include <cstdio>
 2 #include <cstring>
 3 int SG[(1 << 20) + 5];
 4 void rdy(){
 5     int i, j, last, isUS[20];
 6     for (i = 1; i < (1 << 20); ++i){
 7         memset(isUS, 0, sizeof(isUS)); last = -1;
 8         for (j = 0; j < 20; ++j){
 9             if (i&(1 << j)){
10                 if (~last) isUS[SG[i ^ (1 << j) ^ (1 << last)]] = 1;
11             }
12             else last = j;
13         }
14         for (j = 0; isUS[j]; ++j);
15         SG[i] = j;
16     }
17 }
18 int main(){
19     rdy();
20     int t, n, m, p, now, s, w;
21     for (scanf("%d", &t); t; --t){
22         s = 0;
23         for (scanf("%d", &n); n; --n){
24             now = 0;
25             for (scanf("%d", &m); m; --m){
26                 scanf("%d", &p); now |= 1 << 20 - p;
27             }
28             s ^= SG[now];
29         }
30         if (s) puts("YES");
31         else puts("NO");
32     }
33     return 0;
34 }
时间: 2024-10-13 17:56:50

HDU 5724 Chess(国际象棋)的相关文章

HDU 5724 Chess(博弈论)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5724 [题目大意] 给出一个n行,每行有20格的棋盘,棋盘上有一些棋子,每次操作可以选择其中一个棋子,将其移至最左端的空位,两个人轮流操作,无法操作者输,判断游戏胜负. [题解] 首先对于单行20格的游戏,这是一个NIM游戏,将20格的情况状态压缩,对于每种情况递归求其mex集合,计算其sg值,sg值为0的状态为必败态. 而对于可以拆分为多组NIM游戏的游戏,其sg值为拆分出的多组游戏的sg值的

HDU 5724 Chess(SG函数)

Chess Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2605    Accepted Submission(s): 1092 Problem Description Alice and Bob are playing a special chess game on an n × 20 chessboard. There are s

HDU 5724 Chess

因为一行最多只有20个数,也就是说只有(1<<20)种状态,向右移动表示小的数推向了大的数.可以用SG函数预处理出所有情况.然后把每一行的SG函数值异或一下,非零则必胜,否则输. #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<map> #include<set> #

hdu 5724 Chess 博弈

题目链接 一个n行20列的棋盘. 每一行有若干个棋子. 两人轮流操作, 每人每次可以将一个棋子向右移动一个位置, 如果它右边有一个棋子, 就跳过这个棋子, 如果有若干个棋子, 就将这若干个都跳过. 但是棋子不能移出边界. 如果没有办法移动了, 就算输. 问你先走的能否赢. 只有20列, 所以预处理出所有状态的sg值. 然后直接异或就好了. 然后sg[(1<<20)-1] = 0, 这是必输态, 其他的都可以dfs出来, 具体看代码. #include <bits/stdc++.h>

hdu 4832 Chess(dp)

Chess Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 193    Accepted Submission(s): 59 Problem Description 小度和小良最近又迷上了下棋.棋盘一共有N行M列,我们可以把左上角的格子定为(1,1),右下角的格子定为(N,M).在他们的规则中,"王"在棋盘上的走法遵循十字路线

HDU 4832 Chess (DP)

Chess Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 24    Accepted Submission(s): 10 Problem Description 小度和小良最近又迷上了下棋.棋盘一共有N行M列,我们可以把左上角的格子定为(1,1),右下角的格子定为(N,M).在他们的规则中,"王"在棋盘上的走法遵循十字路线.

HDU 4832 Chess 排列组合 DP

Chess Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 351    Accepted Submission(s): 124 Problem Description 小度和小良最近又迷上了下棋.棋盘一共有N行M列,我们可以把左上角的格子定为(1,1),右下角的格子定为(N,M).在他们的规则中,"王"在棋盘 上的走法遵循十字

HDU 4832 Chess

同样是百度之星的题目.刚开始看题目,觉得这是一道搜索的题,于是就萌生了找题解的想法.一开始就没有斗志,当然不会做出这道题的啦. 可是看完题解恍然大悟,原来是DP,而且很简单的一道DP.再一次失败,说明了看题解真的不是一个好习惯.我要改! 我要改!! 其实基本的思想就是把这个二维移动分开,变成一维的移动,最后加上组合数就OK了. 下面的是代码,虽然是自己敲的,但是还是剽窃过来的.... #include <iostream> #include <cstdio> #include &l

百度之星2017 HDU 6114 Chess 组合数学

Chess 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6114 Description 車是中国象棋中的一种棋子,它能攻击同一行或同一列中没有其他棋子阻隔的棋子.一天,小度在棋盘上摆起了许多車--他想知道,在一共N×M个点的矩形棋盘中摆最多个数的車使其互不攻击的方案数.他经过思考,得出了答案.但他仍不满足,想增加一个条件:对于任何一个車A,如果有其他一个車B在它的上方(車B行号小于車A),那么車A必须在車B的右边(車A列号大于車B).现在要问问