关闭

状态压缩与求sg函数

61人阅读 评论(0) 收藏 举报
分类:

题目:hdu5724

题意:给定一个n*20的棋盘,棋盘上有若干棋子。如果一颗棋子右侧为空,则只可以向右移动一格,若非空,则可以移到第一个空的位置,两人轮流操作,不能操作者为输,问先者是否有必胜策略

解答:注意到棋盘只有20列,则可以用状态压缩存储当前的状态。预处理出所有状态的sg值。然后n个抑或一下就好了。

我的代码(每次递归求出当前的sg值):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN = 1 << 21;
int sg[MAXN];
int getnew(int a,int u,int v)
{
    return (a - (1 << u) + (1 << v));
}
int getsg(int a)
{
    if(sg[a] != -1)
        return sg[a];
    int vis[20];
    memset(vis,0,sizeof(vis));
    int ok = 1;
    for(int i = 0;i < 19;i++)
    {
        int temp = 1 << i;
        if(a & temp)
        {
           for(int j = i + 1;j <= 19;j++)
           {
               if(!(a & (1 << j)))
               {
                   vis[getsg(getnew(a,i,j))] = 1;
                   break;
               }
           }
        }
    }
    for(int i = 0;;i++)
    {
        if(!vis[i])
        {
            sg[a] = i;
            return i;
        }
    }
}
void init()
{
    memset(sg,-1,sizeof(sg));
    for(int i = 0;i <= ((1 << 20) - 1);i++)
    {
        sg[i] = getsg(i);
    }
}
int main()
{
    int T,t,n;
    scanf("%d",&T);
    init();
    while(T--)
    {
        int ans = 0;
        scanf("%d",&n);
        while(n--)
        {
            int k;
            int tmp = 0;
            scanf("%d",&k);
            while(k--)
            {
                scanf("%d",&t);
                tmp += 1<<(t-1);
            }
            ans ^= sg[tmp];
        }
        if(ans == 0)
            puts("NO");
        else
            puts("YES");
    }
    return 0;
}

别人的代码(从后往前求sg值。因为求状态值较小的值,它的后继状态的值肯定比当前的状态值大。所以从后往前求就可以了,不需要递归)

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
const int MAXN = 1 << 20;
int n;
int sg[MAXN];
int vis[20];
int getsg(int x)
{
    memset(vis,0,sizeof(vis));
    for(int i = 0;i < 20;i++)
    {
        if(x&(1 << i))
        {
            for(int j = i + 1;j < 20;j++)
            {
                if(!(x&(1 << j)))
                {
                    vis[sg[x^(1 << i)^(1 << j)]] = 1;
                }
            }
        }
    }
    for(int i = 0;i < 20;i++)
        if(!vis[i])
        return i;
}

int main()
{
    int t;
    scanf("%d",&t);
    for(int i = (1 << 20)-1;i >= 0;i--)
        sg[i] = getsg(i);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        int ans = 0;
        for(int i = 0;i < n;i++)
        {
            int q;
            scanf("%d",&q);
            int m = 0;
            while(q--)
            {
                int x;
                scanf("%d",&x);
                m = m | (1 << (x-1));
            }
            ans = ans ^ sg[m];
        }
        if(ans)
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:7462次
    • 积分:815
    • 等级:
    • 排名:千里之外
    • 原创:78篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条