【Light OJ】No More Tic-tac-toe (博弈 SG函数)

题目链接:

http://acm.bnu.edu.cn/v3/problem_show.php?pid=13319


一道典型的求解SG函数的题目,校内组队赛中遇到此题。开题时有比较清晰的解题思路,但是因为不常刷题导致写代码效率低下,一道题的代码写了近3h。最终因为一个没发现的小错误导致没能AC。实力还是太弱,还是需要努力。

先说说我错误的原因。大家知道求SG函数时,我们只需要把那些直接与终止态相连的状态作为递归终点,手算出它们的SG值即可(一般很容易算),剩下的状态的SG函数直接递归求解即可。以此题为例,我用一个结构体p{l,r,len}来表示一个长度为len,左侧方块为l,右侧方块为r的状态,l,r有三种状态0,1,2,分别代表没放标记、放‘X’和放'O'。这样,我们只需要手算出长度为1的状态{0,0,1}、{0,1,1}、{0,2,1}...的答案, 其他状态直接搜索求解即可,但我画蛇添足地手算了len=2时各个状态的SG函数。然后....就算错了....(让我一个人静一静...

所以说不要做一些画蛇添足的事,求SG函数还是让它它自己去算吧。

接下来说一下算过SG函数之后得到的规律:l==r&&r==0时,SG=(len&1)?1:0;l==0||r==0时,SG=len;剩下的情况中,若l==r,则SG=1否则SG=0。


代码:


#define sc scanf
#define pr printf
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define LL long long
using namespace std;
const int N = 200;
const LL mod = 1000000007;
struct p {
    int l,r,len;
    bool operator < (const p& b)const {
        if(len==b.len&&l==b.l)
            return r<b.r;
        else if(len==b.len)
            return l<b.l;
        else return len<b.len;
    }
};

char s[N];
p c[N];
map<p,int> mapp;

int dfs(p a) {
    if(mapp.count(a))
        return mapp[a];

    if(a.len == 0)
        return 0;
    else if(a.len==1) {
        if(a.l==0||a.r==0)
            return 1;
        else if(a.l==a.r)
            return 1;
        else
            return 0;
    } else {
        int ans = 0;
        bool vis[200];
        memset(vis,0,sizeof(vis));
        p p1,p2;
        p1.l = a.l;
        p1.r = 1;
        p2.l = 1;
        p2.r = a.r;
        for(int i=0; i<a.len; ++i) {
            if(a.l==1&&i==0)
                continue;
            if(a.r==1&&i==a.len-1)
                continue;
            p1.len = i;
            p2.len = a.len-i-1;
            vis[dfs(p1)^dfs(p2)] = 1;
        }
        p1.r = 2;
        p2.l = 2;
        for(int i=0; i<a.len; ++i) {
            if(a.l==2&&i==0)
                continue;
            if(a.r==2&&i==a.len-1)
                continue;
            p1.len = i;
            p2.len = a.len-i-1;
            vis[dfs(p1)^dfs(p2)] = 1;
        }
        for(int i=0;; ++i)
            if(!vis[i]) {
                ans = i;
                break;
            }
        mapp[a] = ans;
        return mapp[a];
    }
}

int main() {
    //freopen("input.in","r",stdin);
    int T;
    int cas = 0;
    sc("%d",&T);
    while(T--) {
        sc("%s",s);
        int l = strlen(s);
        int ct=0;
        int tmp = 0;

        c[0].l = 0;
        int num = 0;
        for(int i=0; i<l; ++i) {
            if(s[i]=='X') {
                c[tmp].r = 1;
                c[tmp].len = ct;
                c[++tmp].l = 1;
                ct = 0;
                num++;

            } else if(s[i]=='O') {
                c[tmp].r = 2;
                c[tmp].len = ct;
                c[++tmp].l = 2;
                ct = 0;
                num++;
            }
            if(s[i]!='.')
                continue;
            ct++;
        }
        c[tmp].r = 0;
        c[tmp].len = ct;
        c[++tmp].l = 0;
        ct = 0;
        int res =0;
        for(int i=0; i<tmp; ++i)
            res ^=dfs(c[i]);//打表找规律

        pr("Case %d: ",++cas);
        if(((num&1)&&res)||((!(num&1))&&(!res)))
            puts("No");
        else
            puts("Yes");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值