BZOJ 3895: 取石子
[Submit][Status][Discuss]
Description
Alice和Bob两个好朋含友又开始玩取石子了。游戏开始时,有N堆石子
排成一排,然后他们轮流操作(Alice先手),每次操作时从下面的规则中任选一个:
·从某堆石子中取走一个
·合并任意两堆石子
不能操作的人输。Alice想知道,她是否能有必胜策略。
Input
第一行输入T,表示数据组数。
对于每组测试数据,第一行读入N。
接下来N个正整数a1,a2…an,表示每堆石子的数量。
Output
对于每组测试数据,输出一行。
输出YES表示Alice有必胜策略,输出NO表示Alice没有必胜策略。
Sample Input
3
3
1 1 2
2
3 4
3
2 3 5
Sample Output
YES
NO
NO
HINT
100%的数据满足T<=100, N<=50. ai<=1000
题解
找规律+观察可得,游戏的胜与负与操作次数有关,但一堆只有一个的石子要单独考虑
记忆化搜索+博弈论
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
int T,n,a[MAXN];
bool vis[MAXN][MAXN*1000],f[MAXN][MAXN*1000];
inline bool dfs(int x,int y){
if(vis[x][y]) return f[x][y];
vis[x][y]=1;
if(x==0) return f[x][y]=y&1; //没有一个石子的堆了
if(y==1) return f[x][y]=dfs(x+1,0); //没有多个石子的堆
if(x>=2 && y && !dfs(x-2,y+3)) //将两堆一个的石子合并,之后还能其他堆合并
return f[x][y]=1;
if(x>=2 && !y && !dfs(x-2,y+2)) //同上,但不能再与其他多余一个的堆合并
return f[x][y]=1;
if(x && !dfs(x-1,y)) return f[x][y]=1; //取走一个石子的一堆
if(y && !dfs(x,y-1)) return f[x][y]=1; //取一个大于一个石子的一堆的一个
if(x && y && !dfs(x-1,y+1)) return f[x][y]=1; //将一个石子与多个石子合并
return f[x][y]=0;
}
int main(){
scanf("%d",&T);
while(T--){
int x=0,y=0;
scanf("%d",&n);
for(register int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]==1) x++;
else y+=a[i]+1;
}
if(x==0) {
if((y-1)&1) puts("YES");
else puts("NO");
}
else{
if(dfs(x,max(y-1,0))) puts("YES");
else puts("NO");
}
}
return 0;
}