经典博弈

1、巴什博弈
题意:只有一堆物品,物品的个数为n,每次取1~m个,最后取完者胜。
解题思路:我们可以这样思考,对手最多只能拿m个,最少拿1个,那么我们就去构造m+1个,这样不管对手怎么取,都不可能一次性取完,所以m+1为必败点。

#include <iostream>
#include <math.h>
#include <algorithm>
using namespace std;

int main()
{
   int t;
   cin>>t;
   while(t--)
   {
       int n,m;
       cin>>n>>m;
       if(n%(m+1)==0)cout<<"second"<<endl;
       else cout<<"first"<<endl;
   }

    return 0;
}

2、Fibonacci博弈
题意:1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".
解题思路:打表找规律
n = 2时输出second;
n = 3时也是输出second;
n = 4时,第一个人想获胜就必须先拿1个,这时剩余的石子数为3,此时无论第二个人如何取,第一个人都能赢,输出first;
n = 5时,first不可能获胜,因为他取2时,second直接取掉剩下的3个就会获胜,当他取1时,这样就变成了n为4的情形,所以输出的是second;
n = 6时,first只要去掉1个,就可以让局势变成n为5的情形,所以输出的是first;
n = 7时,first取掉2个,局势变成n为5的情形,故first赢,所以输出的是first;
n = 8时,当first取1的时候,局势变为7的情形,第二个人可赢,first取2的时候,局势变成n为6得到情形,也是第二个人赢,取3的时候,second直接取掉剩下的5个,所以n = 8时,输出的是second;
…………
从上面的分析可以看出,n为2、3、5、8时,这些都是输出second,即必败点,仔细的人会发现这些满足斐波那契数的规律,可以推断13也是一个必败点。


我们打表找规律不难发现这是Fibonacci序列,但如果我们对Fibonacci序列十分了解,题意中的2倍也能使我们的思路向Fibonacci序列上靠。因为序列中每相邻的三个数,第三个数肯定是小于3倍的第一个数的。

#include<iostream>
using namespace std;
int main()
{    
    int fib[50] = {2,3};
    for(int i = 2; i < 45; i++)
        fib[i] = fib[i - 1] + fib[i - 2];
    int n;
    while(cin>>n)
    {   
        if(n==0)break;
        int flag = 1;
        for(int i = 0; i < 44; i++)
        if(fib[i] == n){
            flag = 0;
            break;
        }
        if(flag)cout<<"First win"<<endl;
        else cout<<"Second win"<<endl;
    }
    return 0;
}

3、威佐夫博弈
题意:有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
解题思路:这种博弈与其去分析奇异局势,还不如直接去记公式。假设两堆石子数分别为a,b;如果a=abs(a-b)*(sqrt(5)+1)/2,那么此组数为必败组。

#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    long long n,m;
    while(cin>>n>>m)
    {
        if(n>m)swap(n,m);
        int x=m-n;
        double yy=(1.0+sqrt(5.0))/2;
        if(n==int(x*yy))cout<<"0"<<endl;
        else cout<<"1"<<endl;
    }
    return 0;
}

4、尼姆博弈(非常重要,因为有很多根据它延申的题)
题意:m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,10先取者胜,先取者第1次取时可以从有8个的那一堆取走7个剩下1个,也可以从有9个的中那一堆取走9个剩下0个,也可以从有10个的中那一堆取走7个剩下3个.
解题思路:我们先来看两堆的情况,如果两堆的个数相等的话,那么不管先手的人怎么取,先手必输。这不难理解,那当m>2的时候呢??我们同样可以比拟两堆的情况把前两堆看作一堆,就变成了前两堆与第三堆的比较 (注意:此时不是比较前两堆的个数与第三堆的个数,而是前两堆取完石子后的情况与第三堆比较),以此类推。这样我们就引入了异或运算。
运算规则

相同为0,不同为1,即

1 ^ 1 = 0

0 ^ 0 = 0

1 ^ 0 = 1
异或性质

(1)交换律: A ^ B = B ^ A

(2)结合律: ( A ^ B ) ^ C = A ^ ( B ^ C )

(3)自反性: A ^ B ^ B = A (由结合律可推: A ^ B ^ B = A ^ ( B ^ B ) = A ^ 0 = A)

#include <iostream>

using namespace std;

int main()
{
    int n;
    int x[200005];
    while(cin>>n)
    {
        if(n==0)break;
        int temp=0;
        int s;
        for(int a=0;a<n;a++)
        {
            cin>>x[a];
            temp^=x[a];
        }
        if(temp!=0)
        {
            cout<<"Yes"<<endl;
                    for(int a=0;a<n;a++)

                {
                    s=temp^x[a];
                    if(s<x[a])
                    cout<<x[a]<<" "<<s<<endl;
                }
        }
        else cout<<"No"<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值