原题链接:
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函数、打表找规律相关内容、问题本身并不难,难在博弈论这三个字,看见这三个字就不敢做了,其实只要掌握基本结论,再稍微会点数学就行了。