【题解】大床Nim (2019,5.23)
Description
Sample Input
5 5 5
Sample Output
4
Solution
其实这题就是一个博弈DP,但是我们考虑怎么设计DP状态使我们可以用这些DP状态表示所有博弈状态。
我们发现会对局面造成影响的有第一局面的取法和第二局面的堆数,石子个数什么的。
那么又综合上博弈DP的状态特点设计出一个状态
:
\(f[i][j]\) 表示第一局面还剩下 \(i\) 个石子,第二局面的 \(SG\) 值为 \(j\) 时当前要操作的人是负还是胜。
转移时我们只要枚举当前选多少石子拿到第二局面。
那么怎么转移。
有一个状态并且这个状态的后继中有负的状态那么当前状态就是必赢。
#include<bits/stdc++.h>
using namespace std;
int n,p,K;
const int N=1005,P=1007,M=1034;
int f[N][M];
inline int SG(int x){
if(x<=1) return x;
if((x+1)%4==0) return x+1;
if(x%4==0) return x-1;
return x;
}
inline int dfs(int num,int sg){
if(num==0) return f[num][sg]=(sg!=0);
if(f[num][sg]!=-1) return f[num][sg];
for(int i=1;i<=min(num,K);++i)
if(!dfs(num-i,sg^SG(i*p%P+1))) return f[num][sg]=1;
return f[num][sg]=0;
}
int main(){
freopen("double.in","r",stdin);
freopen("double.out","w",stdout);
scanf("%d%d%d",&n,&p,&K);
memset(f,-1,sizeof(f));
for(int i=1;i<=min(n,K);++i)
if(!dfs(n-i,SG(i*p%P+1))) return printf("%d\n",i),0;
puts("-1");
return 0;
}