$ color{#0066ff}{ 题目描述 }$
Alice和Bob两个好朋含友又开始玩取石子了。游戏开始时,有N堆石子
排成一排,然后他们轮流操作(Alice先手),每次操作时从下面的规则中任选一个:
·从某堆石子中取走一个
·合并任意两堆石子
不能操作的人输。Alice想知道,她是否能有必胜策略。
(color{#0066ff}{输入格式})
第一行输入T,表示数据组数。
对于每组测试数据,第一行读入N。
接下来N个正整数a1,a2…an,表示每堆石子的数量。
(color{#0066ff}{输出格式})
对于每组测试数据,输出一行。
输出YES表示Alice有必胜策略,输出NO表示Alice没有必胜策略。
(color{#0066ff}{输入样例})
3
3
1 1 2
2
3 4
3
2 3 5
(color{#0066ff}{输出样例})
YES
NO
NO
(color{#0066ff}{数据范围与提示})
100%的数据满足T<=100, N<=50. ai<=1000
(color{#0066ff}{题解})
如果合并,石子数不变,如果取,石子数-1,表面上看,结局已经注定,跟操作数的奇偶有关
然而,会有这样的情况,比如1和1
合并,那么操作数-1,如果取走一个1,那么操作数-2!!!这是不一样的
于是我们发现,状态实际上只跟1的堆数和操作数有关
所以,设(sg[x][y])表示当前有x堆是1,除了1的那些堆的操作数共y次的SG值
然后分别讨论所有情况进行转移
因为不涉及多个游戏的合并,所以sg不是0就是1,就不用开vis数组记录什么东西了
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
int n;
int sg[55][52050];
int work(int x, int y) {
if(!x) return y & 1;
if(y == 1) return sg[x][y] = work(x + 1, y - 1);
if(~sg[x][y]) return sg[x][y];
if(x && !work(x - 1, y)) return sg[x][y] = 1;
if(x && y && !work(x - 1, y + 1)) return sg[x][y] = 1;
if(x >= 2 && !work(x - 2, y + 2 + (y > 0))) return sg[x][y] = 1;
if(y && !work(x, y - 1)) return sg[x][y] = 1;
//拿走一个1
//1跟非1合并
//两个1合并
//某个非1堆拿走1个或合并两个非1堆
return sg[x][y] = 0;
}
int main() {
memset(sg, -1, sizeof sg);
for(int T = in(); T --> 0;) {
n = in();
int x = 0, y = 0, z;
for(int i = 1; i <= n; i++) {
z = in();
x += (z == 1);
y += (z > 1) * z;
}
y += n - x - 1;
if(y == -1) y++;
printf(!work(x, y)? "NO
" : "YES
");
}
return 0;
}