zoukankan      html  css  js  c++  java
  • 转|poj2234maches game|博弈论

    以下内容转自精灵的博客。
    时间限制:
        1000ms
    内存限制:
        65536kB

    描述
        Here is a simple game. In this game, there are several piles of matches and two players. The two player play in turn. In each turn, one can choose a pile and take away arbitrary number of matches from the pile (Of course the number of matches, which is taken away, cannot be zero and cannot be larger than the number of matches in the chosen pile). If after a player’s turn, there is no match left, the player is the winner. Suppose that the two players are all very clear. Your job is to tell whether the player who plays first can win the game or not.
    输入
        The input consists of several lines, and in each line there is a test case. At the beginning of a line, there is an integer M (1 <= M <=20), which is the number of piles. Then comes M positive integers, which are not larger than 10000000. These M integers represent the number of matches in each pile.
    输出
        For each test case, output "Yes" in a single line, if the player who play first will win, otherwise output "No".
    样例输入

        2 45 45

        3 3 6 9

    样例输出

        No

        Yes


    (一)这是一道博弈论Nim Game的题目,我们先从简单情况开始分析,再逐步深入。

    (1)只有一堆,先手赢
    (2)两堆,一共2根火柴,后手赢              (取完一堆即可)
      两堆,一共3根火柴,先手赢               (1+2格局,在2个的一堆中取1个,转化为前一种情形)
      两堆,一堆2个,另一堆2个,后手赢 (取完一堆,或者取1个都必输)
      两堆,一堆1个,另一堆3个,先手赢 (1+3格局,在3个的一堆中取2个,转化为2根的情形)
              ...   
      两堆,堆中火柴数目相等,后手赢
      两堆,堆中火柴数目不等,先手赢  
    (3)三堆,一共3根火柴,先手赢
      三堆,一共4根火柴,先手赢
      三堆,一共5根火柴,1.1.3型先手赢   (因为总能够转化到两堆相等的情况)
      三堆,一共5根火柴,2.2.1型先手赢   (因为总能够转化到两堆相等的情况)
      三堆,一共6根火柴,2.2.2型先手赢
      三堆,一共6根火柴,1.1.4型先手赢
      三堆,一共6根火柴,1.2.3型后手赢

    通过简单的分析发现规律并不易寻找,并不是简单的奇偶关系(我就错误地推出过奇偶胜负关系)

    (二)我们开始深入一点

        我们发现在两堆时,我们找到一个有趣的规律,那就是如果两堆数目相等,那么后手方赢,如果两堆数目不等,先手方赢(思考一下为什么?)。但三堆的时候貌似又行不通了,因为如最简单的1+1+1格局,显然是各堆数目相等,但应该是先手方赢。但我们马上会发现,对于三堆的情形,如果各堆数目相等,一定是先手方赢(想清楚为什么?)。进一步推广,我们可以得到如下结论:

    堆为奇数个,各堆中火柴数目相等,一定是先手方赢

    堆为偶数个,各堆中火柴数目相等,一定是后手方赢

    (三)我们开始处理最棘手的部分

    我们现在能够处理,所有堆中火柴都相等的时候的情形,但各堆中火柴不相等呢?这里我们再次看到数学中一些不可思议的联系。我们现在重新审视和定义这个游戏的胜负

    (1)如果轮到某人取火柴的时候,火柴已经没有了,那么此人输,设为P-格局

    (2)如果轮到某人取火柴的时候,他能够取完火柴,那么此人赢,设为N-格局

    (3)如果轮到某人取火柴的时候,他能够将当前格局转化为某个P格局,那么此格局为N-格局

    (4)如果轮到某人取火柴的时候,他不能将当前格局转化为某个P格局,那么此格局为P-格局

    下面我们来推导一个定理:一个格局记为(x1,x2,...,xn),且次序无关,此格局为 P-格局 当且仅当 x1^x2^...^xn = 0.其中^是按位异或运算

    推导:  对应上述4种情形(为了叙述的简洁,并不十分严谨地证明,而是直接假设结论正确,再说明定理的工作机制)

                (1)当(x1,x2,...,xn)中全为0时            ,格局为P-格局,此时x1 ^ x2 ^ ... ^ xn = 0成立。

                (2)当(x1,x2,...,xn)中只有一个不为0,格局为N-格局,此时x1 ^ x2 ^ ... ^ xn = 0不成立。

                (3)当(x1,x2,...,xn)是P-格局时,x1,...,xn不全为0.(反证法)

                                              假设x1 ^ x2 ^ ... ^ xn = p,且p不为0,

                                              记 p 的二进制表示中,最左的1(最高位的1)在从左至右数第 k 位.

                                              由于p是异或运算的结果,那么 x1, x2 , ... , xn中至少有一个数第k位为1,

                                              不妨设 xi 的第 k 位为1,那么 xi ^ p 第 k 位为0,那么xi > xi^p 显然成立.

                                              也就是说,存在某种取法,使i堆的火柴数变化到 xi^p .

                                              题设x1 ^ x2 ^ ... ^ xn = p,那么x1 ^ x2 ^ ... ^ xn ^ p = 0.

                                              那么当前格局可以转化到某个P-格局,也就是说当前格局时N-格局,矛盾

                                              所以,必有p=0.

                    (4)当x1 ^ x2 ^ ... ^ xn = 0时,如果存在某个移动 xi 变化到 xi ’ ,且x1^x2^....^xi ' ^...^xn = 0,

                         那么由异或运算的消去律有,xi = xi ' ,也就是说一根火柴都没取,这不允许的,所以

                         当前格局只能是P格局

    有了这个强大的定理那么上题就很好解决了:

        #include <iostream>  
        using namespace std;  
          
        int main()  
        {  
          
            int M;  
            while(cin>>M)  
            {  
                /*input*/  
                int pile[20] ={};  
                for(int i=0; i<M; ++i)  
                {   cin>>pile[i]; }  
          
                /*calculate*/  
                int result   = 0;  
                for(int i=0; i<M; ++i)  
                {   result ^= pile[i];  }  
          
                /*output*/  
                if(result)cout<<"Yes"<<endl;  
                else      cout<<"No" <<endl;  
            }  
          
            return 0;  
        }  
  • 相关阅读:
    使用Regex.Replace只替换字符串一次
    Socket
    [转载]ASP.NET中在不同的子域中共享Session
    C#构造函数
    C# 的 ArrayList
    [转]决定何时使用 DataGrid、DataList 或 Repeater
    window.showModalDialog弹出对话框刷新问题
    ASP.NET] 选择文件夹的对话框
    网页打印javascript:window.print()
    开展工作
  • 原文地址:https://www.cnblogs.com/Shymuel/p/4649403.html
Copyright © 2011-2022 走看看