HDU 5724 Chess(SG博弈 + 状压)
来源:互联网 发布:圆形进度条js代码 编辑:程序博客网 时间:2024/05/23 12:24
Chess
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1673 Accepted Submission(s): 723
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 integerT(T≤100) , indicates the number of test cases.
For each test case, the first line contains a single integern(n≤1000) , the number of lines of chessboard.
Thenn 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.
The first line contains an integer
For each test case, the first line contains a single integer
Then
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
题意:有一个n行20列的棋盘,棋盘上分布着一些棋子,A、B两人轮流下棋,A先手,每次操作可以将某个棋子放到自己右边的第一个空位(也就是说右边如果已经有子,可以跳过它,没有就右移一步),但最多20列,绝对不能超过棋盘,无棋可走的输。
此题首先要理解SG函数,然后用状压进行处理即可(大部分人的代码大同小异,希望读者能够理解其中的变化)
#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int MAXN = 1e2 + 5;const int MAXM = (1 << 21);int SG[MAXM], Hash[MAXN];void init(){ memset(SG, 0, sizeof(SG)); for(int i = 0;i <= (1 << 20) - 1;i ++){ memset(Hash, 0, sizeof(Hash)); for(int j = 0;j < 20;j ++){ if(i & (1 << j)){ for(int k = j - 1;k >= 0 ;k --){ if(i & (1 << k)) continue; Hash[SG[i ^ (1 << j) ^ (1 << k)]] = 1;//可以用i - (1 << j) + (1 << k),异或的结果是一样的 break;//此处为了处理只向右移动一位 } } } for(int j = 0;j < 20;j ++){ if(!Hash[j]){ SG[i] = j; break; } } }}int T, n, m;int main(){ init(); scanf("%d", &T); while(T --){ scanf("%d", &n); int sum = 0, x; for(int i = 0;i < n;i ++){ scanf("%d", &m); int o = 0; while(m --){ scanf("%d", &x); o |= (1 << (20 - x)); } sum ^= SG[o]; } if(sum){ printf("YES\n"); } else{ printf("NO\n"); } } return 0;}
总结:直到做了这道多校赛的题后,自己才略微了解了SG函数的一些用法和它的原理,曾想自己去下了杭电的PPT去学习博弈,然后再SG函数在那里懵逼一天,虽然其中偶尔想想心中喜欢的女神外,时间基本全扣在上面,现在看来是值得的,整个SG函数基本已经在脑海中形成一种体系,可能还有些模糊,但是经过以后不断的练习,这种模糊感自然而然就会消失掉,而对于多校赛这道非常经典的SG函数加上状态压缩处理的题目,稍微对SG函数做一下自己所理解的一些看法:
1.Hash数组的用法和数组需要开多大
Hash数组在这里本质就是标记当前节点的后续节点有哪些已经出现了,正如SG函数原本定义的那样,每一个SG值表示的是当前后续节点中的最小自然数的值,所以我们只要想清楚了,一个节点最多有多少个后续节点就可以为Hash开多大的数组了。
对于这道题目,一个棋子在20列中移动,撑死也只有20,所以Hash数组开的大小只要比20大一点点就可以了。这里一定要记住的是Hash中的数组标记即Hash[x]中的x和原来的题目其实是没有多大关系的,他表示的你在进行SG处理的时候,针对每一个后续节点序号的标记(每一个SG值节点的后续节点理论上都要从0标记到最后,然后有些后续节点已经在前面一些节点给标记了,所以不需要从标记,但是有些节点还是必须得从0标记)。
这里的x可能个人讲的不是非常清楚,这里引用SG函数的一些定义,希望可以更好的理解(令N = {0, 1, 2, 3, ...} 为自然数的集合。Sprague-Grundy 函数给游戏中的每个状态分配了一个自然数。结点v的Grundy值等于没有在v的后继的Grundy值中出现的最小自然数)
2.SG数组的函数
SG数组,顾名思义英文名走起:Sprague-Grundy ,它存取的值即为一个有向无环图的g(x)=mex{g(y) |y是x的后继节点}。这里不多解释,SG最基本的定义了,个人理解还是非常正常的。
1 0
- HDU 5724 Chess (博弈 状压+sg函数)
- HDU 5724 Chess(SG博弈 + 状压)
- hdu 5724 Chess [状压+SG函数]【博弈】
- HDU 5724 Chess(博弈,SG函数)
- HDU 5724 Chess(SG函数 Nim博弈)
- hdu 5724 Chess(sg博弈)
- HDU 5724 Chess (状压+sg)
- (组合博弈)(sg函数模版)HDU 5724 Chess
- HDU 1524 A Chess Game(SG博弈)
- HDU-5724 Chess(SG函数+状压)
- HDU 5724 Chess (博弈)
- hdu 5724chess 博弈
- hdu 5724 - chess (sg函数)
- hdu 5724 Chess(sg函数)
- hdu 5724 Chess SG函数
- HDU 5724 Chess(博弈&状压)
- (HDU 5724)2016 Multi-University Training Contest 1 Chess(SG函数、博弈)
- HDU ACM 1524 A Chess Game->博弈(SG函数)
- JAVA启动停止Tomcat服务
- 某个公司采用公用电话传递数据,数据是四位的整数,在传递过程中是加密的,加密规则如下:每位数字都加上5,然后用和除以10的余数代替该数字,再将第一位和第四位交换,第二位和第三位交换。
- Struts2 Web 资源获取的四种方式
- 基数排序C/C++
- 重新抛出异常——fillInStackTrace()
- HDU 5724 Chess(SG博弈 + 状压)
- 二进制与逻辑运算
- 利用”异或”运算的性质,对几个字符进行加密并输出密文,然后再解密。加密算法是:密钥是字符’8’,明文的每个字符和密钥进行异或运算,得到密文。密钥和密文的每个字符再次进行异或运算,重新得到明文。
- 【UVA 11624】Fire!(BFS)
- poj 1611 The Suspects
- linux命令
- hdu-2289-Cup
- 02-android之NDK hellojni实例
- #6.Linux的进程管理