博弈
赛中的时候第一反应是结论题,但是跑了几个样例没跑出来
于是试着从算法方面入手。
大概猜测和堆数与石子数的总和有关,但是第一个和第三个样例告诉我应该不是简单的求和关系。
可能是记忆化搜索,但是状态过多实在不知道如何存储,然后就弃疗了……
题解是这样的
发现如果当前没有石子是一个,且(石子堆数 + 石子总数 - 1)为偶数,则是必胜状态。
这是整个算法的基础。
但是现在有一些石子堆只有1个石子,取了该石子,相当于一次进行了两次操作。
所以设dp[i][j],i表示有多少个只有1个石子的石堆,j表示(石子数大于1的石子堆+这些石子堆的石子总和-1)。
值域为0,1。0表示必败,1表示必胜。
那么,接下来就是枚举当前操作进行记忆化搜索。
失分点/学习点:
1. 没有建立好整个模型,没弄清楚博弈最基本的必胜状态。
2. 在记忆化搜索的过程中,状态转移方程比较多没有列全。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 50 + 2;
const int MAXM = 1000 + 2;
int dp[MAXN][MAXM * MAXN];
int dfs(int one, int sum)
{
// printf("one = %d, sum = %d\n", one, sum);
// system("pause");
if(sum == 1) one++, sum--;
if(dp[one][sum] != -1) return dp[one][sum];
if(one == 0) return dp[one][sum] = (sum % 2 == 0 ? 0 : 1);
else if(sum && dfs(one - 1, sum + 1) == 0) return dp[one][sum] = 1;
else if(dfs(one - 1, sum) == 0) return dp[one][sum] = 1;
else if(sum && dfs(one, sum - 1) == 0) return dp[one][sum] = 1;
else if(one >= 2 && sum && dfs(one - 2, sum + 3) == 0) return dp[one][sum] = 1;
else if(one >= 2 && sum == 0 && dfs(one - 2, sum + 2) == 0) return dp[one][sum] = 1;
else return dp[one][sum] = 0;
}
int main()
{
memset(dp, -1, sizeof dp);
int T; scanf("%d", &T);
for(int cas = 1 ; cas <= T ; cas++) {
int n; scanf("%d", &n);
int sum = 0, one = 0;
for(int i = 0 ; i < n ; i++) {
int u; scanf("%d", &u);
if(u == 1) one++;
else sum += u + 1;
}
// dfs(one, sum);
// for(int i = 0 ; i <= one ; i++) {
// for(int j = 0 ; j <= sum + one + 1 ; j++) printf("%d ", dfs(i, j));
// printf("\n");
// }
if(sum) sum--;
// printf("cas = %d, one = %d, sum = %d\n", cas, one, sum);
if(dfs(one, sum) == 1) printf("Case #%d: Alice\n", cas);
else printf("Case #%d: Bob\n", cas);
}
return 0;
}