Description
Alice和Bob两个好朋含友又开始玩取石子了。游戏开始时,有N堆石子
排成一排,然后他们轮流操作(Alice先手),每次操作时从下面的规则中任选一个:
·从某堆石子中取走一个
·合并任意两堆石子
不能操作的人输。Alice想知道,她是否能有必胜策略。
Input
第一行输入T,表示数据组数。
对于每组测试数据,第一行读入N。
接下来N个正整数a1,a2…an,表示每堆石子的数量。
Output
对于每组测试数据,输出一行。
输出YES表示Alice有必胜策略,输出NO表示Alice没有必胜策略。
Sample Input
2
3
1 1 2
2
3 4
3
2 3 5
3
1 1 2
2
3 4
3
2 3 5
Sample Output
YES
NO
NO
NO
NO
HINT
100%的数据满足T<=100, N<=50. ai<=1000
题解:考虑如果不存在石子数为1的堆。
设这种状态下操作数为x,显然x等于石子总数加操作数减1。
可以证明当x为奇数时先手必胜。当x为偶数时先手必败。
如果只有1堆石子,该结论显然成立。
如果有多堆石子,每堆石子个数都大于1,并且x为偶数,下面我们证明这样先手必败。
1.如果先手选择合并两堆石子,那么每堆石子的个数依然大于1,x变为奇数。
2.如果先手选择从一堆石子数大于2的堆中拿走一枚石子,那么同上每堆石子个数依然大于1,x变为奇数。
3.如果先手选择从一堆石子数等于2的堆中拿走一枚石子,那么后手可以合并剩下的1枚石子到任意一个堆。
那样x的奇偶性不变,每堆石子的个数依然大于1.
综上所述,结论成立。
然后考虑如果存在石子数为1的堆.我们设石子数为1的堆的个数为x,其余堆的个数为y.
令f[x][y]为这种状态下先手必胜或是必败。
每次转移枚举所有可能的操作。记忆化搜索即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,f[55][50055],T,x,a,b,vis[55][50055];
int dfs(int a,int b){
if (a==0) return b&1;
if (b==1) return dfs(a+1,0);
if (vis[a][b]) return f[a][b];vis[a][b]=1;
if (a&&!dfs(a-1,b)) return f[a][b]=1;
if (a&&b&&!dfs(a-1,b+1)) return f[a][b]=1;
if (a>=2&&!dfs(a-2,b+2+(b?1:0))) return f[a][b]=1;
if (b&&!dfs(a,b-1)) return f[a][b]=1;
return f[a][b]=0;
}
int main(){
scanf("%d",&T);
while (T--){
scanf("%d",&n);a=0;b=-1;
for (int i=1;i<=n;i++){
scanf("%d",&x);
if (x==1) a++;
else b+=x+1;
}
if (b==-1) b=0;
if (dfs(a,b)==1) printf("YES\n");
else printf("NO\n");
}
}