题目大意
给定 堆石头,每次可以最多可以取走与该堆石子互质个数量的石子,现两人都以最优策略进行,问谁能获胜
解题思路
首先这道题不存在平局,且都是以最优策略进行的,这类问题我么称为,
(尼玛)博弈问题,我们先来看一看该问题的原型
给定 件物品,每次可以取走若干件,但不能不取,问先手能否获胜!
这就是 博弈问题的原型, 博弈有一个定理,那就是
当先手必胜时,当且仅当
反之亦然
但是这道题限制了我们最多只能取与其互质的个数,所以就引入了一个新的东西,
(傻狗)函数,
函数可以解决该问题的限制因素,那么具体怎么构建呢?看下面:
设 表示面对有 个石头的堆时能取走的个数
若
为合数时,得到
若
为质数时,得到
接下来,我们可以筛一波素数,在筛素数的过程中计算出每个数的最小质因子,然后令所有石子数变成其对应的 函数,就可以求解了
代码
#include<cstdio>
#define N 1000005
#define Il inline
#define re register
#define max(a,b) a>b?a:b
using namespace std;int prime[N],sg[N],ans,t,n,a;
Il void Make_sg()
{
prime[1]=1;
for(int i=2;i<N;i++)
if(!prime[i])
{
prime[i]=i;
for(int j=i;j<N;j+=i) if(!prime[j]) prime[j]=i;//筛质因数,同时标记每个数的最小质因子
}
int maxn=0;
for(int i=1;i<N;i++)
{
if(prime[i]==i) sg[i]=maxn+1;
else sg[i]=sg[prime[i]];
maxn=max(maxn,sg[i]);//用maxn来维护最大的SG
}
}
int main()
{
Make_sg();
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);ans=0;
for(re int i=1;i<=n;i++)
scanf("%d",&a),ans^=sg[a];//NIM博弈定理
if(ans) puts("Alice");else puts("Bob");//输出
}
}
后记
记得当年东莞市赛有一道题,给定 堆物品,每次可以取走一堆或其中的一部分,给定先手的人,问谁能赢?
其实这一道题也是一个 博弈问题,只不过其的 均为1,所以只要谁先手谁就能赢,这也是一个简单的 博弈问题的应用
该算法时间复杂度为: