hdu 5724 SG函数+状压(多校)

题意:
一个棋盘有n行,每行20格子,都有一些棋子,两个人轮流进行这个操作:选择某一行一个棋子移动到该行右边第一个空的格子。不能进行的人输。问先手是否能赢。

分析:
SG函数的应用,当时自己做的时候没做出来QAQ。终结点是这一行没有棋子可以走,即0,然后逆推出其他结点的SG函数。每一行的状态看成是一个结点,然后把状态二进制压缩,1表示有棋子,0表示空格。

#include<cstdio>
#include<cstring>
using namespace std;
int sg[1<<21],vis[21];
int getSG(int x)
{
    memset(vis,0,sizeof(vis));
    for(int i=20;i>=0;i--){
        if(x&(1<<i)){
            int t=x;
            for(int j=i-1;j>=0;j--)if(!(x&(1<<j))){
                t^=(1<<i)^(1<<j);
                vis[sg[t]]=1;break;
            }
        }
    }
    for(int i=0;i<=20;i++)if(!vis[i])return i;
}
int main()
{
    memset(sg,0,sizeof(sg));
    for(int i=0;i<(1<<20);i++)
        sg[i]=getSG(i);
    int T;scanf("%d",&T);
    while(T--){
        int n,m,x,ans=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d",&m);
            int st=0;
            while(m--){
                scanf("%d",&x);
                st|=1<<(20-x); //是从右边开始的,21这个位置为0
            }
            ans^=sg[st];
        }
        printf("%s
",ans?"YES":"NO");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/01world/p/5762833.html