题意:有n个石子,Alice和Bob依次拿石子,到一方不能再拿即为失败。Alice能够拿的个数有x种,分别是a0,a1…ax。Bob能够拿的个数有y种,分别是b0,b1…bx。Alice先手
1<=n<=1000000000
x<=5,y<=5且1<=ai,bi<=5,ai之间互不相同,bi之间也互不相同
题解:
这道题很不同寻常的是,两者拿的个数种类不一定相同,这也意味着不能单纯地用一维的dp来实现。考虑加维度,显然,我们的目的是区分开当前到底是谁拿,所以第二维为0/1分别表示当前轮到Alice或Bob出手。时间复杂度O(n),但很显然这样并不能过这道题。
所以我们考虑周期性
“x,y<=5且ai,bi<=5”很显然就是突破口,一种情况d[i][x]的答案只会从前5个(d[i-5][!x]–d[i-1][!x])的情况得到。那么不难发现对一种情况有影响的只是前5个情况,所以就可以用前5个的情况来储存当前状态。状态相同即一定为一个周期,用一个十位二进制数来存储,所以不同的状态总数为210个,这样就可以把循环的次数降到210以下。(详见代码)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
#define SF scanf
#define PF printf
#define MAXS 10
#define MAXN 1000000
using namespace std;
int a[MAXS],b[MAXS],d[MAXN][2],was[(1<<(11))+10],n,x,y;
int main(){
memset(was,-1,sizeof was);
SF("%d",&n);
SF("%d",&x);
for(int i=1;i<=x;i++)
SF("%d",&a[i]);
SF("%d",&y);
for(int i=1;i<=y;i++)
SF("%d",&b[i]);
sort(a+1,a+x+1);
sort(b+1,b+y+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=x&&i-a[j]>=0;j++)
if(d[i-a[j]][0]==0){
d[i][1]=1;
break;
}
for(int j=1;j<=y&&i-b[j]>=0;j++)
if(d[i-b[j]][1]==0){
d[i][0]=1;
break;
}
if(i>5){
int m=0;
for(int j=0;j<5;j++)
for(int k=0;k<2;k++){
m*=2;
m+=d[i-j][k];
}
if(was[m]==-1)
was[m]=i;
else{
int p=i-was[m];
n-=(n-i)/p*p;
}
}
}
if(d[n][1])
PF("Alice");
else
PF("Bob");
}