巴什博弈(P4101 [HEOI2014] 人人尽说江南好

今天在洛谷随机跳题,跳到了这道八什博弈题,我感觉蛮好的,来写一篇题解。

P4101 [HEOI2014] 人人尽说江南好

题目大意:有 n 堆石子,每次可以将两堆石子合并成一堆,但合并后的石子数量不能超过m,最后无法合并石子失败,问先手是否必胜。

首先,如果没有m的限制的话,操作次数为 n-1 次,当 n 为偶数时,先手必胜。但是加入了 m 的这个限制,就需要有所改变了,当 n <= m 时,m的限制不存在,像上面的求解过程就行了,如果 n > m 的话,就要考虑 m 的限制了。

我们可以让整个的博弈过程尽可能的长,及两个人都很聪明,都会选择对自己有利的方式,那么当最长的操作是必胜局面时,无论后手怎么操作,先手都会把局面重新变为必胜局面,反之亦然。而要想整个的操作次数最大,则每堆的都要尽可能的堆满m个。

先说结论,最终的操作次数为 ans=n-1-n/m+(n%m==0); 当 ans 为奇数时,先手必胜。

下面讲讲操作次数为什么是这个公式,n-1 为不考虑 m 的总操作次数,n/m为多出来没有合并的堆,多出几个就代表操作次数减少多少个,当 m|n 时,就要增加一个状态,因为剪掉的操作次数是多出来的一堆,当 m|n 时,多出来的一堆为 n/m -1。

那么就会有个问题了,既然使得最大操作次数的办法是使每堆都尽可能的达到 m 堆,那如何才能让对手无论怎么弄都能达到 m 堆呢?

假设 n 足够的大,且该局面是先手必胜的:

1,m为偶数,先手先将两个 1 合并为 2(下面称该堆为大堆) ,后手只有两个选择,将 1 放入那个大堆,或者也将两个 1 合并为 2 ,如果是前者,那么先手只需要将一个 1 合并到大堆即可,如果是后者,那么只需要将那个 2 合并到大堆即可,可知,大堆的石子个数一定是成偶数倍增加的,及一定能组成大堆。

当组成为一个大堆时,就到了后手组大堆,而后手要想使得不让“每堆尽可能的堆成 m 个”,那么只能将两个 1 合并为 2 ,此时先手在第一次时,将 1 合并到大堆中,后续每次都将后手合并的 2 合并到大堆中,到大堆达到 m-1 个时,再将1合并过去,后面以此类推,都是能合并为大堆的。

2,m 为奇数,则要将大堆的个数维持在先手为奇数的情况,及当大堆为偶数时,先手要主动合并 1 到大堆中,之后再将后手创建的 2 合并到大堆中,当大堆为奇数堆时,则要将已有的偶数堆合并进去,或者创建偶数堆。

如果该局面是先手必败的话,那么后手可以仿照上面的方法,使得每个堆的数量都尽可能的达到 m 个,代码如下

#include
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
signed main(){
    ios;
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        if((n-1+n/m+(n%m==0))&1)cout<<0<

博弈论通常都是代码简单,但是推理思路困难,这类题对于思路的提升还是蛮好的。

你可能感兴趣的:(算法)