初识博弈

初始了解了一些博弈,根据特定的规则,学习了一些只会和规律

首先是   巴什博弈:

HDU1846,最基本的先手后手赢问题,对于一堆石子我要采取什么拿的方法才能让对方最后无子可那呢,那就是在我拿完之后尽可能的让n是m+1的倍数(n是总数,m 是我能拿的上限)

 int flag = n % (m + 1);
        if(flag >= 1)cout<<"first"<<endl;
        else cout<<"second"<<endl;

然后HDU2149,换了一种问法,多了一种特殊的判断可拿的大于已有的

int flag = m % (n + 1);
       if(flag == 0)cout<<"none"<<endl;
       else
       {
           printf("%d",flag);
           if(flag == m)
           {
               for(int i = flag + 1;i <= n;i++)
               {
                   printf(" %d",i);
               }
           }
           printf("\n");
       }

接下来又去看了HDU2147,换成了一种棋盘的问题,这里又学习了必败点P和比胜点N的关系,

定理:

     一、所有终结点都是必败点P

    二、所有一步能走到必败点P的就是N点;

    三、通过一步操作只能到N点的就是P点;

那就想一想棋盘上的必败点和必胜点的分布

NNNNNNNNN
PNPNPNPNP
NNNNNNNNN
PNPNPNPNP
NNNNNNNNN
PNPNPNPNP
NNNNNNNNN
PNPNPNPNP

总结处比较简单的规律了



cin>>n>>m;
        int flag = n % (m + 1);
        if(!flag)cout<<"Rabbit"<<endl;
        else cout<<"Grass"<<endl;

如果行数和列数都为奇数的时候,那么必败

然后是斐波那契博弈
1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。

斐波那契取石子问题,有一个定理齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。

分析后,会得到如下规律,一开始如果是斐波那契数的话,那么后手胜利,不是的话,先手胜利,查找斐波那契数可以用二分查找

接下来威佐夫博弈
这是两堆物品,出现了奇异局势(必败局势)—— (a,b)

对于a,b两堆判断位必败局势的方法是



if(a > b)swap(a,b);
        c = int((b - a)*((sqrt(5.0) + 1) / 2));
        if(c == a)puts("0");
        else puts("1");

如何得出的我也还没消化,暂且记录以下结论
接下来是Nim博弈
取N堆石子的问题

我们可以将每一个数字写成二进制形式,如果每一个二进制位上1的个数都相等(称为平衡态),无论采取怎样的拿法,只会把其变为

一个存在某些二进制位上1的个数不相等的状态(称为非平衡态)

用异或去做,n堆石子的异或和如果位0那么先手必败,否则先手胜利,这是HDU1850

 for(int i = 1;i <= s;i++)
        {
            cin>>a[i];
            ans ^= a[i];
        }
        if(ans == 0)
        {
            puts("0");
            continue;
        }

知道ans != 0后这个题还没完,还问我第一步有几种方案


for(int i = 1;i <= s;i++)
        {
            int k = ans ^ a[i];
            if(k < a[i])cnt++;
        }

也就是遍历每一堆石子,异或后的值如果小,那么就能得到,这一堆的石子够我那了
然后我又补了一道NIM博弈的题目ZOJ3964,也是NIM问题,但是有了限制,对于限制的分析成立我们的解题点

对于限制,如果我们不去消除,那么最后只会让对方对我们进行控制,我们就必败

所以借着先手的优势,能一步解决我们的限制,才有赢得希望,所以我们得知道:

如果限制多余1个那么不用考虑,必败
那就一个限制呢,就得看看能不能一次性解决问题,对于限制2,我只能拿偶数,恰好那一堆是奇数,完了,限制解除不了,必败,恰巧位偶数呢,就可以按照正常的Nim了,只不过先后手要注意一下

对于限制1,就没那么多事了,一次拿光也好,剩下一个也好,我和对手都是平等的了,不存在劣势了,可以一搏
没有限制,正常的NIM操作

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],b[N],n;
bool judge(int cnt1,int cnt2)
{
    int tot = 0;
    if(cnt1 == 1)
    {
        for(int i = 1;i <= n;i++)
        {
            if(b[i] == 1 && a[i] > 1)//相当于bob先手得nim
            {
                if(a[i] & 1)
                {
                    tot ^= 0;
                }
                else
                {
                    tot ^= 1;
                }
            }
            else
            {
                tot ^= a[i];
            }
        }
        if(tot == 0)return false;
        else return true;
    }
    else if(cnt2 == 1)
    {
        for(int i = 1;i <= n;i++)
        {
            if(b[i] != 2)//前面已经判断力如果a[i]为奇数得话,必输
            {
                tot ^= a[i];
            }
        }
        if(tot == 0)return false;
        else return true;
    }
    else
    {
        for(int i = 1;i <= n;i++)
        {
            tot ^= a[i];
        }
        if(tot == 0)return true;
        else return false;
    }
}
int main()
{
    int T;
    while(~scanf("%d",&T))
    {
        while(T--)
        {
            scanf("%d",&n);
            int cnt2 = 0,cnt1 = 0,cnt0 = 0;
            for(int i = 1;i <= n;i++)
            {
                scanf("%d",&a[i]);
            }
            for(int i = 1;i <= n;i++)
            {
                scanf("%d",&b[i]);
                if(b[i] == 2)
                {
                    cnt2++;
                    cnt0++;
                    if(a[i] & 1)cnt0 = 99;
                }
                if(b[i] == 1 && a[i] > 1)//a[i] = 1时相当于没有限制
                {
                    cnt1++;
                    cnt0++;
                }
            }
            if(cnt0 > 1 || judge(cnt1,cnt2))
            {
                printf("Bob\n");
            }
            else
            {
                printf("Alice\n");
            }

        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值