博弈


前言

总结一下最近碰到的博弈题


提示:以下是本篇文章正文内容,下面案例可供参考

一、巴什博弈

巴什博弈:一堆物品有n个,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。


分析

显然当n=m+1时,后手必赢;因此当n=(m+1)*r+s(r为任意自然数,s≤m)时,先手能拿s个物品使得目前剩余物品变为n’=(m+1)*r时,后手取k个,先手就取m+1-k(k<=m)个,则接下来剩余的物品数变为n’’=(m+1) *(r-1),以后保持这样的取法,那么先取者肯定获胜。同理可得后手必赢的条件。
由上述可推出,只要物品数量满足n%(m+1)==0则后手胜利,否则先手胜利

变形

当我们规定,如果最后取光者输时,即取到最后一个物品的人输,此时只要物品数量满足(n-1)%(k-1)==0则后手必赢。
除了取物品,报数也可以运用此类博弈,例如两个人轮流报数,每次至少报一个,最多报m个,谁能报到n者胜。


代码如下(示例):
#include <iostream>

using namespace std;

int main()
{
    int t,n,k;
    while(~scanf("%d",&t)){
         while(t--){
            scanf("%d%d",&n,&k);
            if(n%(k+1)==0)
                printf("second win!\n");
            else
                printf("first win!\n");
         }
    }
    return 0;
}

二、尼姆博弈

有m堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。


分析

我们先来简化一下,假设有两堆若干个物品:若数量相同,无论先手拿多少,后手只需在另一堆拿和先手同样多的物品(此时后手必胜);若数量不同,则先手取其中一堆若干个至两堆数量相同(此时先手必胜)。
假设此时有三堆若干个物品,我们用(a,b,c)来表示某种局势:无论谁面对奇异局势,都必然失败,显然(0,0,0)为奇异局势;(0,n,n)是第二种奇异局势,此时与只有两堆相同数量物品相同;(1,2,3)也是奇异局势,无论自己如何拿,接下来对手都可以将其变为(0,n,n)的情形。


于是就有大佬想到了把这种规律和二进制联系在一起,至于是怎么联系的,再来一点点分析。
首先,每堆物品的数量一定是非负整数,就可以用二进制来表示。我们先假设有三堆石子(3,6,9),分别用二进制表示为11,110,1001

11=10+1;
110=100+10;
1001=1000+1;

再转换为十进制:3=2+1,6=4+2,9=8+1;所以现在可以看作是3堆变成了6堆,其中两堆为1,两堆为2,在这两堆里先手必输,而剩下两堆为4和8,此时先手从8个一堆中拿走4个即可,此时先手必胜。
从上述变形中我们可以发现先手在1,2这两堆中达成了(n,n)局面所以必输,有没有方法能在二进制的计算中将他们直接舍去呢?于是就用到了异或。

先来说下异或
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。并且异或运算会直接将十进制转化为二进制。
所以上述三堆的案例就可以用3^ 6^ 9!=0,所以先手必赢。

也就是说当有n堆石子时,
只要满足a^ b^ c^ d^…!=0,先手必赢。
代码如下(示例):

#include <iostream>

using namespace std;

int main()
{
    long long m;
    while((scanf("%lld",&m)!=EOF)&&m){
            long long n[200000],i;
            long long x=0;
            for(i=0;i<m;i++){
                cin>>n[i];
                x=x^n[i];
            }
            if(x){
                cout<<"Yes"<<endl;

            }
            else cout<<"No"<<endl;
    }
    return 0;
}

题目

链接: link.
代码如下(示例):

#include <iostream>

using namespace std;

int main()
{
    long long m;
    while((scanf("%lld",&m)!=EOF)&&m){
            long long n[200000],i,t;
            long long x=0;
            for(i=0;i<m;i++){
                cin>>n[i];
                x=x^n[i];
            }
            if(x){
                cout<<"Yes"<<endl;
                for(i=0;i<m;i++){
                    t=x^n[i];
                    if(t<n[i])
                        cout<<n[i]<<" "<<t<<endl;
                }

            }
            else cout<<"No"<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值