题意:
现在有n堆石子,每堆石子分别有A1,A2,A3...An个
现在有两种操作:
1.选一堆非空石子将其数量减一
2.合并两个非空石子
Alice 和 Bob 轮流进行操作,先取完的获胜,Alice先操作,问谁能获胜
思路:
首先需要发现几个性质:
1.所有给出的数据都可以转换成 (A,X)的状态。 A为石子数量为1的石子堆数目,X为其余 所有非1的石子堆
的和加上 它们的 数目 - 1。
2.如果没有石子数量为1的石子堆,那么最后的结果为 (sum+n-1)&1。
4.如果只有两个数量为1的石子堆的数目,那么此时为(2,X)的形式,为了避开给对手(1,X)的必胜局势,他一定
会选择合并两个1,或者从X中拿走一个
5.只有在(A,2)的局势下,才能转移到(A+1,0)的局势,也就是说这有一个2的局势下选择从2中拿走一个1有可能
可以改变胜负态。
注意到上面这些,那么就开始设计状态转移方程了:
当X=0:1.A=0 : dp[A][X]=0
(0,0)为败势
2.A<3 dp[A][X]=1
(1,0)和(2,0)都为胜势
3.else dp[A][X]=(!dp[A-2][2]) | (!dp[A-1][0])
可以合并两个1或者拿走一个1
当X!=0:1.A=0 dp[A][X]=X&1
当0个1的时候看X的奇偶
2.A=1 dp[A][X]=1
当1个1的时候必胜
3.else dp[A][X] = (!dp[A-1][X]) | (!dp[A-2][X+3]) | (!dp[A-1][X+1])
三种操作分别对应着 拿走1,合并2个1,合并1和X
①X!=2 dp[A][X] |= (!dp[A][X-1])
从X拿走1,此时状态为(A,X-1)
②X==2 dp[A][X] |= (!dp[A+1][0])
同样从X拿走1,此时状态为(A+1,0)
最后的顺序需要注意(3.②)的情况,当处理(A,2)(A>0)的情况时,需先处理(A+1,0)的情况。
代码:
#include<bits/stdc++.h>
using namespace std;
bool dp[51][55005];
inline void oper(int a,int b){
if(b==0){
if(a==0)dp[a][b]=0;
else if(a<3)dp[a][b]=1;
else dp[a][0] = (!dp[a-2][2])|(!dp[a-1][0]);
return ;
}
if(a==0)dp[a][b] = b&1;
else if(a==1)dp[a][b]=1;
else {
dp[a][b] = (!dp[a-1][b])|(!dp[a-2][b+3])|(!dp[a-1][b+1]);
if(b!=2)dp[a][b]|=(!dp[a][b-1]);
else dp[a][b]|=(!dp[a+1][0]);
}
}
void init(){
int m = 55000;
for(int i=0;i<=50;i++){
for(int j=0;j<=m;j++){
if(i&&j==2)oper(i+1,0);
oper(i,j);
}
}
}
int main()
{
init();
int T,t=0;
scanf("%d",&T);
while(T--){
int n,x;
scanf("%d",&n);
int ans = 0 ;
int one = 0 ;
while(n--){
scanf("%d",&x);
if(x!=1){
if(ans)ans++;
ans += x;
}
else one++;
}
printf("Case #%d: %s\n",++t,dp[one][ans]?"Alice":"Bob");
}return 0;
}