http://acm.hdu.edu.cn/showproblem.php?pid=5724
题意:20列n行的一个棋盘,起始上面有一些棋子,每次玩家可以移动向右,如果右边有连续x个就跳过。。问玩家一输赢情况。。
思路:发现和SG函数那个图的模型很像,所以联想到SG函数可以。。(玄学。。我并不懂博弈)。。那么我们就可以按照SG函数的定义,用mex那个操作来记忆化搜索一下,过程基本上就是一个状压DP,当然也可以倒着递推啦。。复杂的大概1e7.。
(核心思路就是想到SG函数,使用记忆化搜索的方法求出所有状态的SG值,然后就愉快的异或一下子。。)
代码:
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
int ans[(1<<20)+5];
int vis[(1<<20)+5];
int mov(int sit,int idx){
int nxt=sit&(~(1<<idx));
idx--;
while(idx>=0&&(sit&(1<<idx))) idx--;
if(idx<0) return -1;
nxt=(nxt|(1<<idx));
return nxt;
}
int dfs(int sit){
vis[sit]=1;
int mex[21]={0};
for(int i=0;i<20;i++){
if(sit&(1<<i)){
int nxt=mov(sit,i);
if(nxt!=-1){
if(vis[nxt]==1) mex[ans[nxt]]=1;
else mex[dfs(nxt)]=1;
}
}
}
for(int i=0;i<21;i++){
if(!mex[i]) return ans[sit]=i;
}
}
int main()
{
int sit=0;
ans[0]=0;
for(int i=19;i>=0;i--){
sit=sit|(1<<i);
dfs(sit);
}
int t;
scanf("%d",&t);
while(t--){
int n,m,idx;
int ret=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&m);
int st=0;
for(int j=0;j<m;j++){
scanf("%d",&idx);
st|=(1<<(20-idx));
}
ret^=ans[st];
}
if(ret==0)
printf("NO\n");
else
printf("YES\n");
}
return 0;
}