BJFU 1010——博弈

描述

博弈题目向来是比较难的,来尝试一下吧~

有两堆扑克牌,数量任意(均大于0),可以不同。两人开始博弈,轮流抽取扑克牌。规定每次取牌有两种方式,第一种方式是任选一堆扑克牌,并从中取走任意张(大于0);第二种是在两堆扑克牌中同时取走相同张数(大于0)的牌,每次取牌时必须选择一种方式,最后把牌全部取完的一方胜利。现在给出初始两堆扑克牌的数目,假设博弈的两人都绝顶聪明,问是先取者胜还是后取者胜。

输入

输入包含多组测试数据,每组测试数据占一行,由两个正整数a和b组成,表示初始两堆扑克牌的数量,其中a和b都不大于1000000000。

输出

对于每组测试数据,请输出一行说明是先取者胜利还是后取者胜利。若选取者胜利,请输出“The first one will win!”,否则请输出“The second one will win!”。

样例输入

2 1
8 4
4 7

样例输出

The second one will win!
The first one will win!
The second one will win!



此题为威佐夫博弈






问题:首先有两堆石子,博弈双方每次可以取一堆石子中的任意个,不能不取,或者取两堆石子中的相同个。先取完者赢。

分析:首先我们根据条件来分析博弈中的奇异局势

      第一个(0 , 0),先手输,当游戏某一方面对( 0 , 0)时,他没有办法取了,那么肯定是先手在上一局取完了,那么输。

第二个 ( 1  , 2  ),先手输,先手只有四种取法,

1)取 1 中的一个,那么后手取第二堆中两个。

2)取 2 中一个,那么后手在两堆中各取一个。

3)在 2 中取两个,那么后手在第一堆中取一个。

4)两堆中各取一个,那么后手在第二堆中取一个。

可以看出,不论先手怎么取,后说总是能赢。所以先手必输!

第三个 ( 3 , 5 ),先手必输。首先先手必定不能把任意一堆取完,如果取完了很明显后手取完另一堆先手必输,那么

假如看取一堆的情况,假设先手先在第一堆中取。 取 1 个,后手第二堆中取4个,变成(1 ,2)了,上面分析了是先手的必输局。

 取 2 个,后手第二堆中取3个,也变成( 1 , 2)局面了。

假设先手在第二堆中取,取 1 个,那么后手在两堆中各取 2 个,也变成 ( 1 , 2 )局面了。

   取 2 个 ,那么后手可以两堆中都去三个, 变成 ( 0 , 0)局面,上面分析其必输。

   取  3  个,后手两堆各取 1 个 ,变成( 1 , 2)局面了。

  取 4 个,后手在第一堆中取一个,变成( 1 , 2)局面了。

可见不论先手怎么取,其必输!

第四个(4  , 7),先手必输。

自己推理可以发现不论第一次先手如何取,那么后手总是会变成前面分析过的先手的必输局面。

那么到底有什么规律没有呢,我们继续往下写。

第四个 ( 6 ,10  )

第五个 ( 8 ,13)

第六个 ( 9 , 15)

第七个 ( 11 ,18)

会发现他们的差值是递增的,为 0 , 1 , 2, 3, 4 , 5 , 6, 7.....n

而用数学方法分析发现局面中第一个值为前面局面中没有出现过的第一个值,比如第三个局面,前面出现了 0  1 2,那么第三个局面的第一个值为 3 ,比如第五个局面,前

面出现了 0  1  2 3 4 5 ,那么第五个局面第一个值为6。

再找规律的话我们会发现,第一个值 = 差值 * 1.618 

而1.618 = (sqrt(5)+ 1) /  2 。

大家都知道0.618是黄金分割率。而威佐夫博弈正好是1.618,这就是博弈的奇妙之处!



下面来看看威佐夫博弈常见的三类问题:


1)给你一个局面,让你求是先手输赢。

有了上面的分析,那么这个问题应该不难解决。首先求出差值,差值 * 1.618 == 最小值 的话后手赢,否则先手赢。(注意这里的1.618最好是用上面式子计算出来的,否则精

度要求高的题目会错)

 

2)给你一个局面,让你求先手输赢,假设先手赢的话输出他第一次的取法。

       首先讨论在两边同时取的情况,很明显两边同时取的话,不论怎样取他的差值是不会变的,那么我们可以根据差值计算出其中的小的值,然后加上差值就是大的一个值,当

然能取的条件是求出的最小的值不能大于其中小的一堆的石子数目。

      加入在一堆中取的话,可以取任意一堆,那么其差值也是不定的,但是我们可以枚举差值,差值范围是0 --- 大的石子数目,然后根据上面的理论判断满足条件的话就是一种合理的取法。



因此记得一个公式即可



q=(sqrt(5.0)+1.0)/2.0;
    while(scanf("%lf%lf",&a,&b)!=EOF)
    {
        if(a<b){t=a;a=b;b=t;}
        if((int)b==(int)((a-b)*q))
            printf("The second one will win!\n");
        else
            printf("The first one will win!\n");


这个问题属于博弈论中的策略游戏,通常被称为“汉诺塔游戏”的变种。为了让计算机先手必胜,需要设计一种策略使其总是能保证无论对手如何选择,都能保持对剩余牌数的优势。 首先,我们可以将总牌数视为3的倍数加1(这里是54=18*3+0),这是因为每一轮玩家取走的牌数之和始终会减少到一个3的倍数。所以当轮到计算机时,它应该确保剩下的牌数是一个非零的3的倍数。 计算机的策略可以分为两个部分: 1. **初始策略**:计算机先取走总数减去3(因为它是非零3的倍数),使得剩下的是一个完整的3的倍数,例如如果开始是54张,则计算机拿走51张。 2. **后续策略**:每次对手取n张(1≤n≤4),计算机则取\( 3 - n \)张。这样,总牌数始终保持为3的倍数,而当对手取完所有单数(如3、6、9...)之后,只剩下偶数张牌,此时对手无法再满足规则取走最后1张。 以下是简单的Python代码实现这个策略: ```python def is_winnable(total_cards): # 判断剩余牌数是否为3的倍数 if total_cards % 3 != 0: return False # 计算电脑需要先取走的牌数 first_move = (total_cards // 3) * 3 + 3 return first_move <= total_cards # 游戏开始时,判断并打印第一局是否为电脑必胜 initial_cards = 54 if is_winnable(initial_cards): print("电脑有必胜策略") else: print("电脑没有必胜策略") # 根据实际情况,你可以在此基础上构建游戏循环,模拟两人交替取牌的过程 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值