题意:有n堆石头,可以把多堆合并成一堆,也可以从其中一堆取走一个石子。
如果没有个数为1的堆,那么一次肯定取不完一个堆,这个堆一定会被合并
所以能合并的都一定会被合并
所以剩下的一定会是只有大小为1的堆和一些多余1的堆
我们把个数多余1的堆看做新的一个大堆,堆里面石子的个数代表的是它的操作步数
因为每次只能取走一个石子
即合并的步数+石子的个数=∑ai + n - 1
下面我们就要考虑大小为1的堆的个数,因为每一个1都是等价的,就可以记忆化搜索了
//---------------------------------------------------//
其中的操作包括:
把某堆只有一个的,取走
把不是一个的,取走一个
把两堆只有一个的,合并
把某堆只有一个的,合并给不是一个的
采用记忆化搜索
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int sg[51][50005], n;
//i表示1个的堆,j表示多个的堆
int getsg(int i, int j){
if(~sg[i][j])return sg[i][j];
if(j == 1)
return sg[i][j] = getsg(i + 1, 0);
sg[i][j] = 0;
//在i中取走1个
if(i && !getsg(i-1, j))
sg[i][j] = 1;
//在j中取走1个
else if(j && !getsg(i, j-1))
sg[i][j] = 1;
//把i中的1个合并到j中
else if(i && j && !getsg(i-1, j+1))
sg[i][j] = 1;
//把i中的2个合并
else if(i > 1 && ((j == 0 && !getsg(i-2, j+2)) || (j && !getsg(i-2, j+3))))
sg[i][j] = 1;
return sg[i][j];
}
int main(){
int test;
memset(sg, -1, sizeof sg);
scanf("%d", &test);
while(test --){
scanf("%d", &n);
int x, one = 0, sum = 0;
for(int i = 1; i <= n; i ++){
scanf("%d", &x);
if(x == 1)
one += x;
else
sum += x + 1;
}
if(sum)
sum --;
puts(getsg(one, sum) ? "YES" : "NO");
}
return 0;
}