HDU 5724 博弈,SG函数

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
");
        else
            printf("YES
");
    }
    return 0;
}



原文地址:https://www.cnblogs.com/zhangxianlong/p/10672551.html