• PKU-1704-Georgia and Bob


    题目链接

    http://poj.org/problem?id=1704

    这个题目是个好题,没有两下子是做不出的,其中考到,要你排序,如何把题目化成我们熟知的东西,

    在这个题中我开始用选择法排序,他给我个wang answer,我表示不理解,但用sort AC了,谁知道可以告诉我一声,表示不甚感激!

    题目的思路(来自网上)很遗憾自己开始连题目的看不懂,思路更不用说了。

    这题是暑假ACM集训时做的,是尼姆博弈的变形运用,最初做这题时,把任意两棋子之间的距离当做了尼姆博弈中的一堆石子(或纸牌)。这想法太天真了,经高手指点后,发现要把棋子配对。现在假设处于这种情况——每两个棋子都是紧挨着的。这种状态是种必败的状态,无论你移动哪个棋子,下个玩家会移动另一个棋子,使他们再次紧挨着。这样移动前后可以看成是同种状态,所以我们可以假设配对的棋子中前一个棋子是不动的,每次移动后一个棋子减少距离,谁能最后使配对的棋子全部紧挨谁就胜利(就同尼姆博弈中取走一堆中的石子一样,谁最后取完谁胜)。细心的人会发现,如果棋子数量是奇数怎么配对,如果从前往后配对,最后一个棋子的状态就不好固定了,所以从后往前陪。第一个棋子可以想象为与左边界配对。poj中的输入样例是从从小到大的,但题中没给出说明,所以我们要对输入的数据排序,才能得到正确的解。


    开始看到此题的时候完全没有什么思路,但是看”寻找必败态——一类问题的快速解法“的解释如下:

    分析:
           乍一看这一题棋子移动还要受其他棋子的限制,好像无法求出通解,但仔细分析会发现别有洞天。
                        
            一个棋子每一次向左移的最大步数是固定的,而且随着移动减少,不是和取石子很像么?那么和取石子的区别在哪呢?就在于每一次移动时都会让右边相邻的那颗棋子移动空间变大,这样就和取石子只减不增有所不同了,我们应该怎样解决这个问题呢?
            我们并不放弃将其与我们熟悉的取石子对应,但我们将策略做小小的变动:
                        
            将棋子从右端向左端每相邻两个分为一对,如果只剩一个就将棋盘左端加一格放一颗棋子与之配对,这样配对后好像和以前没有什么区别,但决策时就方便多了,因为我们大可不必关心组与组之间的距离,当对手移动一组中靠左边的棋子时,我们只需将靠右的那一颗移动相同步数即可!同时我们把每一组两颗棋子的距离视作一堆石子,在对手移动两颗棋子中靠右的那一颗时,我们就和他玩取石子游戏,这样就把本题与取石子对应上了。
             本例说明有许多模型看似复杂,但经过一些巧妙的变换,便可以转化成一些我们熟悉的模型,同时也充分体现了博弈的灵活。
             如果说前面的例子是介绍这种思想的运用的话,下面的方法就是讲这种思路的优越性了,因为这一题并不是走的“手推小数据=〉猜想=〉证明”的老路,而是直接利用性质推导必败条件

    其实看了上面的分析之后还不是很明白,在网上搜了一下如下的解释比较容易理解:

    /*由于任何两个相邻的棋子只与他们之间的空位有关,所以可以转化为普通的Nim游戏:我们可以把这些空位看作是石子数,谁取得了最后一个空位,谁就是赢家。*/

    我的代码

    这里n==1的情况要单独考虑。

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    using namespace std;

    int main(void)
    {
    int t,n,a[10005],k,i,j;
    scanf("%d",&t);
    while(t--)
    {
    k=0;
    a[0]=0;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    /*for(i=1;i<=n;i++)
    {
    for(j=i+1;j<=n;j++)
    {
    if(a[i]>a[j])
    a[i]=a[i]^a[j]^(a[j]=a[i]);
    }
    }*/
    if(n==1&&a[1]==1)
    printf("Bob will win ");
    else if(n==1&&a[1]!=1)
    printf("Georgia will win ");
    else
    {
    if(n%2)
    {
    for(i=1;i<=n;i++,i++)
    k=k^(a[i]-a[i-1]-1);
    }
    else
    {
    for(i=2;i<=n;i++,i++)
    k=k^(a[i]-a[i-1]-1);
    }
    if(k==0)
    printf("Bob will win ");
    else
    printf("Georgia will win ");
    }
    }
    return 0;
    }

    这题很好,是个好题。

  • 相关阅读:
    求求你,快去学习吧!!
    研究生英语读写译----topic3
    SQL----where 和 on 的区别
    SQL----语句执行顺序
    SQL----Inner Join、 Outer Join、Cross Join理解
    将一般的数值转换为金额格式(分隔千分位和自动增加小数点)
    伪元素 before 和 after 各种妙用
    抽空笑一笑
    别笑抽咯
    JavaScript继承方式详解
  • 原文地址:https://www.cnblogs.com/liudehao/p/3989207.html
Copyright © 2020-2023  润新知