博弈-小花梨的取石子游戏

传送门

时间限制:1000ms 空间限制:512MB

Description

小花梨有?堆石子,第?堆石子数量为???堆石子顺时针编号为1 − ?(如图)。

游戏将进行?轮,每轮游戏单独进行,互不干扰,每轮初始时第?堆石子数目为??

?轮从编号为?的那堆石子为起点,顺时针来取石子。两人轮流取石子,不可不取,最少取

一个石子,最多把当前这一堆取完,只有取完一堆后才走到下一堆石子。走完一圈后石子都

被取完,不能取石子的人就失败。假设两人以最优策略进行取石子操作,请分别输出?轮游

戏是先手胜还是后手胜。

Input

第一行为正整数?,表示石子的堆数 (1 ≤ ? ≤ 100000)

第二行输入?个正整数表示每一堆的石子数目??(1 ≤ ?? ≤ 109 )

Output

输出?行,第?行表示第?轮游戏的结果。如果先手胜则输出"?????",后手胜输出"??????"

Example

Sample Input Sample Output

3

2 1 3

First

Second

First

2

2 2

First

First

Note

样例1

游戏进行3

1轮游戏石子堆下标的顺序为1 2 3,此时石子数目按顺序为2 1 3,先手胜

2轮游戏石子堆下标的顺序为2 3 1,此时石子数目按顺序为1 3 2,后手胜

3轮游戏石子堆下标的顺序为3 1 2,此时石子数目按顺序为3 2 1,先手胜

 

 

 

 

题解:

这道题不是尼姆博弈论(Nimm Game),因为在进行运算的时候顺序是给定的

举个例子:现在有两个数据,分别为3和4,如果想赢,那么只要满足先取就行了,因为对于3,一开始取2,那么第二个人只能取1,这个时候第一个人再把第二堆的4给取走,这样就赢了,也就是说只要满足所有的数字都是大于1的,就一定是先取者肯定胜利。

那现在数据变成三个的话,又会怎么样呢?

1:数据分别为:3 1 4 ,因为有一个1存在,那么如果说现在第一个人在取的时候想赢,按照上面的方法,他取走3中的两个数组,现在数据变成 1 1 4,这样第二个人只能取  1,现在数据变成 1 4,那么紧接着第一个人也是只能取1,现在数据变成4.......最后可以看出,肯定是第二个人赢。

那也就是说,在这种情况下,两个人的胜负情况发生了改变

不要被我误导~~~这句话其实是不正确的,因为我们知道在进行博弈的过程中,我们每个人都是最聪明的那一个,既然第一个人在刚开始从3中拿两个数据的时候就已经输了,那为什么不在这一部分进行一个改变呢?直接把3给拿走完不就好了吗,这样到最后,胜利的还是1.

 

综上所述,只要后面出现一个比一大的数字,那么不管它后面的数字情况如何,第一个人都能胜利。

进行一个引申:

2:  1 1 2

这种情况下,前面呢有两个1,2%2=0,第一个人先取能够保证胜利。

刚才后面有一的时候,我们可以通过前面的数字进行调节,使得在当前情况下还能够胜利,那么现在我们只需计算从头开始出现多少个1就能知道第一个人先取还是后取才能保证胜利了



#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=200005;
int a[maxn];
int number[maxn];
int main()
{
    int n;
    cin>>n;
    for(int i=0; i<n; ++i)
    {
        scanf("%d",&a[i]);//输入
    }
    int cnt=n-1;
    for(int i=0;i<n;i++)
    {
        a[++cnt]=a[i];//把环变成线(2倍)
    }
    for(int i=0; i<n; ++i)
    {
        /*
        
        因为担心数据范围过大,在这里进行一个优化,我们要计算的是从某一个数字开始,
        它的后面出现了多少个连续的1,假如说现在第一个数字后面出现了10个连续的1,那
        么第二个数字后面的9个连续的1就是确定的了,不需要再进行计算了(直接跳过)
        
        不过我的这种方法是把长度精确到n了,可以直接报下面的判断区间开到2*n,只需在
        最后判断当前的number是否大于n就行了。
        
        */
        if(i==1)
            for(int j=i; j<=n+i; ++j)
            {
                if(a[j]==1)
                    number[i]++;
                else
                    break;
            }
        
        else
        {
            if(number[i-1]>=1)
                number[i]=number[i-1]-1;
            for(int j=i+number[i]; j<=n+i; ++j)
            {
                if(a[j]==1)
                    number[i]++;
                else
                    break;
            }
        }
    }
    for(int i=0 ;i<n; ++i)
    {
        if(number[i]%2==0)
            printf("First\n");
        else
            printf("Second\n");
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值