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 <stdio.h>
#include <string.h>
#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;
}