博弈随笔(未完待续)

都是一年前做过博弈巨水题  题解也写过了

这里在列出来 再看一下博弈

HDU - 1730 

把黑白子之间的距离看成石子数,每次距离缩短,就相当于拿走了一部分石子,如果对手将一枚棋子往两侧移,那么你只需要再把这枚棋子往中间移相同的步数,就等同于这枚棋子没有移动,那么我们就可以认为石子只会减少不会增加,所以就变成了NIM问题。

这个想法很好 

 

HDU - 2999

就是一个sg

注意后继情况   若一次操作把整个集合分成了多个部分  那就和这个题的代码一样 sg异或一下 如下 (记得有个珠子的题 也是这样)

        for(int j = 0; j < n; j++)
        {
            if(i - a[j] < 0) break;
            vis[sg[i - a[j]]] = 1;
            for(int k = 1; k <= i - a[j] - 1; k++)
                vis[sg[k] ^ sg[i - a[j] - k]] = 1;
        }

 

 

HDU - 3980 

诺 这就是那个珠子的题

只不过这个是一个串  而不是链

那n - w 后是不是就是链了

就是先手先拿一次 

然后再处理 

        int n, m;
        cin>> n >> m;
        printf("Case #%d: ",++kase);

        if(n < m)
        {
            cout<< "abcdxyzk" <<endl;   //后手
            continue;
        }
        init(n-m, m);   //n - m后就相当于HUD - 2999中排队的情况   
        if(sg[n-m])
            cout<< "abcdxyzk" <<endl; 
        else
            cout<< "aekdycoin" <<endl;

 

 

HDU - 1524 

有向图博弈

图上的博弈和普通的解法一样

这里v是u的后继情况

而出度为0的点v没有后继结点 所以此时sg[v] = 0;

int mex(int u)
{
    if(sg[u] != -1) return sg[u];
    bool vis[maxn];            //这个标记数组要放在里面 和普通求sg一样 每个点都有自己的一个vis 因为普通sg是循环所以放在外面就可以 而这里是递归 所以要放在里面
    mem(vis, 0);
    for(int i=head[u]; i!=-1; i=Node[i].next)
    {
        node e = Node[i];
        sg[e.v] = mex(e.v);    //去找后继状态
        vis[sg[e.v]] = 1;
    }
    for(int i=0; ; i++)
        if(!vis[i])
            return i;
    return 0;

}

 

 

HDU - 1851 

有n堆,每堆最多取m个,最少取1个

分而治之就是个Bash

可以直接每堆Bash然后异或  也可以sg 

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        cin >> n;
        int w, l;
        int ret = 0;
        for(int i = 0; i < n; i++)
        {
            cin >> w >> l;
            ret ^= (w % (l + 1));
        }
        if(ret != 0)
            cout << "No" << endl;
        else
            cout << "Yes" << endl;

    }

    return 0;
}
View Code

 

int sg[maxn];
int n, u, v;
void mex()
{
    bool vis[maxn];
    mem(sg, 0);
    for(int i=1; i<=u; i++)
    {
        mem(vis, 0);
        for(int j=1; j<=v; j++)
        {
            if(i < j) break;
            vis[sg[i-j]] = 1;
        }
        for(int j=0; ; j++)
            if(!vis[j])
            {
                sg[i] = j;
                break;
            }
    }
}

int main()
{
    int T;
    cin>> T;
    while(T--)
    {
        int res = 0;
        cin>> n;
        for(int i=0; i<n; i++)
        {
            cin>> u >> v;
            mex();
            res ^= sg[u];
        }
        if(res) cout<< "No" <<endl;
        else
            cout<< "Yes" <<endl;


    }


    return 0;
}
View Code

 

 

HDU - 1849

受1730的影响,这个是不是就可以看作就是nim博弈

异或就好了

 

总结SG的模板

int sg[maxn];
//如果有多堆而每堆拿上界||下界都一样 那么就可以只用一次SG,石子总数选最大值
//如果每堆拿的上界||下界不一样,那么就要每堆都进行一次SG
void SG()
{
    mem(sg, 0);
    bool vis[maxn];
    for(int i=1; i<=100; i++)     //石子总数 一般取最大值即可
    {
        mem(vis, 0);            //每次更新vis
        for(int j=1; j<=i; j++) //每轮可以减去的个数遍历
        {
            vis[sg[i-j]] = 1;   //后继状态
            int tot = i-j;
            for(int k=1; k<tot; k++)       //链式中断后继状态
                vis[sg[k]^sg[tot-k]] = 1;
        }
        for(int j=0; ; j++) //找到不在集合内且最小的
            if(!vis[j])
            {
                sg[i] = j;
                break;
            }
    }
}

 

posted @ 2019-04-27 22:31  WTSRUVF  阅读(259)  评论(0编辑  收藏  举报