jzoj3819 [NOI2015模拟9.9]取石子

Description


Alice和Bob两个好♂朋友又开始玩取石子游戏了。游戏开始时,有N堆石子
排成一排,然后他们轮流操作(Alice先手),每次操作时从下面的规则中
任选一个:
1.从某堆石子中取走一个
2.合并任意两堆石子
不能操作的人输。Alice想知道,她是否能有必胜策略。

30% T<=10,N<=5,ai<=3
60% T<=100,N<=20,ai<=100
100% T<=4000,N<=50,ai<=1000

Solution


这种类规律题可以先从简单情况想起。对于一堆的情况胜负由奇偶性决定,两堆可以看成是一堆加上一个石子(合并操作)
继续往下会出现问题,对于1颗石子的堆,在取走后只减少了1步操作,即0颗石子的堆不能合并。那么分别记录1颗石子的堆数、剩下不为1的石子操作数的总和。n这么小直接爆搜,担心过不了就记忆化

操作分别有合并两个1的堆、合并1和另一个不为1的堆、取走一个1、取走不为1的堆中的一个

Code


#include 
#include 
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int N=50005;

int rec[405][N];
int cnt,sum,n;

int dfs(int a,int b) {
    if (!a) return b&1;
    if (b==1) return dfs(a+1,b-1);
    if (rec[a][b]!=-1) return rec[a][b];
    if (a&&!dfs(a-1,b)) return rec[a][b]=1;
    if (a&&b&&!dfs(a-1,b+1)) return rec[a][b]=1;
    if (a>=2&&!dfs(a-2,b+2+(b!=0))) return rec[a][b]=1;
    if (b&&!dfs(a,b-1)) return rec[a][b]=1;
    return rec[a][b]=0;
}

int main(void) {
    memset(rec,-1,sizeof(rec));
    int T; scanf("%d",&T);
    while (T--) {
        scanf("%d",&n);
        cnt=0; sum=-1;
        rep(i,1,n) {
            int x; scanf("%d",&x);
            if (x==1) cnt++;
            else sum+=x+1;
        }
        if (sum==-1) sum=0;
        if (!dfs(cnt,sum)) puts("NO");
        else puts("YES");
    }
    return 0;
}

你可能感兴趣的:(c++,搜索)