【博弈论】SRM701PartisanGame

来源:互联网 发布:矩阵双竖线 编辑:程序博客网 时间:2024/06/15 20:41

题意:有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个的情况来储存当前状态。状态相同即一定为一个周期,用一个十位二进制数来存储,所以不同的状态总数为2^10个,这样就可以把循环的次数降到2^10以下。(详见代码)

#include<cstdio>#include<cstring>#include<algorithm>#include<climits>#define SF scanf#define PF printf#define MAXS 10#define MAXN 1000000using 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");}
0 0