CodeForces-604E Lieges of Legendre

原题链接:

https://vjudge.net/problem/CodeForces-604E

题意:
两人(K和N)进行博弈论模型游戏·(去掉花里胡哨的说法,什么牛堆什么的,用概率论里经常用到的取球表示):有n箱球,每箱球里有ai个球,K和N两人轮流取(K先手),两人可以执行以下两个步骤
1、从一个箱子里拿走一个球;
2、将有2*x个球的箱子(偶数)替换成k个·有x个球的箱子;
设以最优方法移动,拿走最后一个球的人赢。

输入:n,k ,a1~an;
输出:赢得人(K或N)

读题后的直观思路:
典型的博弈论模型,sg打表找规律,稍微有些变化,在基本流程下(根据题意sg打表找规律->公式求解ans->根据先手情况给出输赢)稍作修改内容即可(最经典的模型就是取球游戏,两人轮流取球,拿走最后一个球的人赢,本题加了个限定条件:一次只能取走一个球;和一个取球方式的变化:增加了一种取球方式如题意)

具体思路:首先知道这样一个结论:如果sg(a1)^ sg(a2)^…… ^ sg(an)==0,先手必败,(sg函数的定义应该不用说了吧,mex 、后继啥的,引入这个定义的过程很复杂 只知道怎么用就行;这个结论是博弈论的经典结论,推导无敌复杂,我也说不清,也是只知道怎么用就行)我这里用ans表示这个结果,如果ans== 0,K先手必败,即输出N;ans==1先手必胜,输出K

这种题一般有两种思路求sg函数的值:1、打表(推荐)2、DFS(不推荐且不会用);

本题的操作(2)可以理解为变换后新增了k个状态,只需算sg()时变化一下就行(本题与经典例题的区别也在于此)

这道题我打表做的,打表代码:(这种题的难点其实就在这里,如何根据经典模型变形需要思考一下,这里看出来规律了这道题就解决了)

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n, k;
    cin >> n >> k;
    int sg[1001];
    sg[0] = 0;
    for (int i = 1; i <= 1000; i++)
    {
        if (i % 2 == 1) //奇,只能执行(1)操作
        {
            if (sg[i - 1] == 0)
                sg[i] = 1;
            else
                sg[i] = 0;
        }
        else //偶,可以执行(2)操作
        {
            int a = sg[i - 1];//(1)操作
            int b;//(2)操作
            if (k % 2 == 1)
                b = sg[i / 2];
            else
                b = 0; //偶数个相同的数异或一定为0

            for (int j = 0; j <= 2; j++)//sg只有0 1 2三种取值
                if (a != j && b != j)
                {
                    sg[i] = j;
                    break;
                }
        }
    }
    for (int i = 0; i <= 1000; i++)
        cout << sg[i] << " ";
    return 0;
}

输出:

在这里插入图片描述
在这里插入图片描述

如果还看不出来 就多试几个k
规律:如下函数 不想写了

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int gam1(int t)//根据打表结果找出规律
{

    if (t == 1 || t == 3)
    {
        return 1;
    }
    if(t&1||t == 2)//k是奇数,装*写法
    {
        return 0;
    }
    if (t == 4)
    {
        return 2;
    }
    if (gam1(t / 2) == 1)
        return 2;
    return 1;
}
int main()
{
    ll n, k;
    ll a[100001];
    cin >> n >> k;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    int ans = 0;
    if (k & 1)//如果k是奇数
    {
        for (int i = 0; i < n; i++)
        {
            ans ^= gam1(a[i]);
        }
    }
    else
    {
        for (int i = 0; i < n; i++)
        {
            if (a[i] == 1)
            {
                ans ^= 1;
            }
            else if (a[i] == 2)
            {
                ans ^= 2;
            }
            else if ((a[i] & 1))
            {
                ans ^= 0;
            }
            else
                ans ^= 1;
        }
    }
    if (ans)
        cout << "Kevin" << endl;
    else
        cout << "Nicky" << endl;

    return 0;
}

总结:
这是一道很有代表性的题,考察博弈论 sg函数、打表找规律相关内容、问题本身并不难,难在博弈论这三个字,看见这三个字就不敢做了,其实只要掌握基本结论,再稍微会点数学就行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值