Description
Solution
这道题在考场上做的时候特别有思路 但还是没有做出来
我们考虑每一堆都大于1的情况
我们发现合并的本质就是在保证数量不变的情况下切换先后手
所以,如果堆数-1+石子总数为偶数则后手胜,为奇数则先手胜
再来考虑有1的情况
这个时候如果要分类讨论就会变得相当复杂
所以我们考虑记忆化搜索
设一个状态 a[x][y]表示为1的有x堆,剩下的石子堆的操作总数为y(y=堆数+总数-1)
然后仔细分类转移即可
具体可以看代码
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int n,t,fl,x,y,i,a[60][50005],v[60][50005],aa[60],sum;
int read(){
int sum=0;
char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9'){
sum=sum*10+c-'0';
c=getchar();}
return sum;
}
inline bool dfs(int x,int y){
if (!x) return (y&1);
if (y==1) return dfs(x+1,0);
if (v[x][y]) return a[x][y];
v[x][y]=1;
if (x>=2&&y&&!dfs(x-2,y+3)) {
a[x][y]=1;
return 1;}
if (x>=2&&!y&&!dfs(x-2,y+2)){
a[x][y]=1;
return 1;}
if (x&&y&&!dfs(x-1,y+1)){
a[x][y]=1;
return 1;}
if (y&&!dfs(x,y-1)){
a[x][y]=1;
return 1;
}
if (x&&!dfs(x-1,y)){
a[x][y]=1;
return 1;
}
a[x][y]=0;
return 0;
}
int main(){
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
t=read();
while (t){
t--;
fl=1;
sum=0;
n=read();
fo(i,1,n){
aa[i]=read(),sum+=aa[i];
if (aa[i]==1) fl=0;}
if (fl) if ((sum+n-1)&1) printf("YES\n"); else printf("NO\n");
else {
x=0,y=0;
fo(i,1,n) if (aa[i]==1) x++; else y+=aa[i]+1;
if (dfs(x,max(y-1,0))) printf("YES\n"); else printf("NO\n");
}
}
}