题目大意:
有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 <stdio.h> #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; }