题目大意:
有K堆石子,每堆有Ki个,两人的操作可以是:
1 从某一堆拿走一个 如果该堆在此之后没有石子了,就消失
2 合并两个堆
求是否先手必胜,先手胜输出Alice,否则输出Bob
思路:
这道题读完后毫无头绪,推了半天也推不个所以然来,参看大神代码后,感觉就是一个记忆化搜索啊,唉,知识学多了不会用还是白搭,还得多做题啊!
这里我们把数字分成 1,2,大于等于3的奇数,大于等于4的偶数四类。
这样分的原因在于,一个大于3的奇数是实际上等价于3的;因为每当对手减一个,自己也减一个,就又变回了一个大于3的奇数,最终变成3。同理,所有大于2的偶数等价于4。
所以我们用dp[a][b][c][d]表示有a个1,b个2,c个3,d个4是不是一个必胜态,然后动规求解就好了。
代码:
#include
#define N 51
bool dp[N][N][N][N] = {0};
bool vis[N][N][N][N] = {0};
int F(int a, int b, int c, int d) // 一个类似记忆化搜索的过程
{
if(!vis[a][b][c][d]){ // 如果后继状态为P状态,则此状态为N状态
if(a >= 1 && !F(a - 1, b, c, d)) dp[a][b][c][d] = 1; // 从 a 中某堆拿走一个,消失
if(b >= 1 && !F(a + 1, b - 1, c, d)) dp[a][b][c][d] = 1; // 从 b 中某堆拿走一个,变成 a
if(c >= 1 && !F(a, b + 1, c - 1, d)) dp[a][b][c][d] = 1; // 从 c 中某堆拿走一个,变成 b
if(d >= 1 && !F(a, b, c + 1, d - 1)) dp[a][b][c][d] = 1; // 从 d 中某堆拿走一个,变成 c
if(a >= 2 && !F(a - 2, b + 1, c, d)) dp[a][b][c][d] = 1; // 合并 a 中的两堆,变成 b 类的一堆
if(b >= 2 && !F(a, b - 2, c, d + 1)) dp[a][b][c][d] = 1; // 合并 b 中的两堆,变成 d 类的一堆
if(c >= 2 && !F(a, b, c - 2, d + 1)) dp[a][b][c][d] = 1; // 合并 c 中的两堆,变成 d 类的一堆
if(d >= 2 && !F(a, b, c, d - 2 + 1)) dp[a][b][c][d] = 1; // 合并 d 中的两堆,变成 d 类的一堆
if(a >= 1 && b >= 1 && !F(a - 1, b - 1, c + 1, d)) dp[a][b][c][d] = 1; // 合并 a、b 中的一堆,变成 c 类的一堆
if(a >= 1 && c >= 1 && !F(a - 1, b, c - 1, d + 1)) dp[a][b][c][d] = 1; // 合并 a、c 中的一堆,变成 d 类的一堆
if(a >= 1 && d >= 1 && !F(a - 1, b, c + 1, d - 1)) dp[a][b][c][d] = 1; // 合并 a、d 中的一堆,变成 c 类的一堆
if(b >= 1 && c >= 1 && !F(a, b - 1, c - 1 + 1, d)) dp[a][b][c][d] = 1; // 合并 b、c 中的一堆,变成 c 类的一堆
if(b >= 1 && d >= 1 && !F(a, b - 2, c, d - 1 + 1)) dp[a][b][c][d] = 1; // 合并 b、d 中的一堆,变成 d 类的一堆
if(c >= 1 && d >= 1 && !F(a, b, c - 1 + 1, d - 1)) dp[a][b][c][d] = 1; // 合并 c、d 中的一堆,变成 c 类的一堆
vis[a][b][c][d] = 1;
}
return dp[a][b][c][d];
}
int main()
{
int loop, n, t, ct = 1;
int a, b, c, d, ans; // a 表示只有一个石子的堆数,b 表示有只有两个石子的堆数,c 表示有奇数个石子的堆数, d 表示有偶数个石子的堆数
scanf("%d", &loop);
while(ct <= loop){
scanf("%d", &n);
a = b = c = d = 0;
for(int i = 0; i < n; i ++){
scanf("%d", &t);
if(t == 1) a ++;
else if(t == 2) b ++;
else if(t % 2 == 1) c ++;
else d ++;
}
ans = F(a, b, c, d);
printf("Case #%d: ", ct ++);
if(ans)
printf("Alice\n");
else
printf("Bob\n");
}
return 0;
}