• 【BZOJ1022】[SHOI2008] 小约翰的游戏(Anti-Nim)


    点此看题面

    大致题意: 与普通(Nim)游戏相比,改为拿到最后一个石子的人输,求谁有必胜策略。

    前言

    为做一道博弈论的题目开始恶补博弈论。。。

    感觉现在理解能力不太行,就这么简单一道题看题解还看了半天。。。

    分类讨论

    对于这道题,我们需要分三类进行讨论:

    1. 每堆只有一个石子。显然此时每人每次必然是将一堆取走,因此偶数堆时先手赢,奇数堆时后手赢。若用(SG)函数表示,则(SG=0)时先手赢,(SG>0)时后手赢。
    2. 只有一堆石子个数大于(1)。则假如除此之外还剩奇数堆石子(个数都为(1)),先手可以把这堆石子取完;假如除此之外还剩偶数堆石子,先手可以留下(1)个石子。两种方式都把局面转化成了第一种情况里奇数堆的情况,因此先手(后手的后手)就必胜了。若用(SG)函数表示,显然一个大于(1)的数和若干(1)的异或值大于(0),因此(SG>0)时先手赢。
    3. 有大于一堆石子个数大于(1),即一般情况。考虑在不断取石子的过程中,必然有某一刻第三类情况被转化为了第二类情况(注意,不可能直接跳过第二类转化为第一类情况),而第二类情况相当于是(SG>0)时获胜。因此,二人都需要尽可能使自己的(SG)值大于(0),于是此时这个问题就变成了普通(Nim)游戏:(SG>0)时先手赢,(SG=0)时后手赢。

    综上所述,先手赢的条件为每堆只有一个石子时(SG=0),或存在石子个数大于(1)的堆时(SG>0)

    代码

    #include<bits/stdc++.h>
    #define Tp tmeplate<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 50
    using namespace std;
    int n;
    int main()
    {
    	RI Tt,i,x,SG,cnt;scanf("%d",&Tt);W(Tt--)
    	{
    		for(SG=cnt=0,scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&x),SG^=x,x>1&&++cnt;//统计SG值,cnt记录1的个数
    		puts((cnt?SG:!SG)?"John":"Brother");//若有1,则SG>0时先手胜;若无1,则SG=0时先手胜
    	}return 0;
    }
    
  • 相关阅读:
    在单向链表中删除指定的key
    双向链表反转
    单向链表反转
    认识异或运算
    二分查找
    插入排序
    冒泡排序
    选择排序
    go 语言环境安装
    欧几里得算法
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1022.html
Copyright © 2020-2023  润新知