3157. 取球博弈
题目链接
3157. 取球博弈
两个人玩取球的游戏。
一共有 \(N\) 个球,每人轮流取球,每次可取集合 \({n_1,n_2,n_3}\) 中的任何一个数目。
如果无法继续取球,则游戏结束。
此时,持有奇数个球的一方获胜。
如果两人都是奇数,则为平局。
假设双方都采用最聪明的取法,第一个取球的人一定能赢吗?
试编程解决这个问题。
输入格式
第一行 \(3\) 个正整数 \(n_1,n_2,n_3\),空格分开,表示每次可取的数目。
第二行 \(5\) 个正整数 \(x_1,x_2,…,x_5\),空格分开,表示 \(5\) 局的初始球数。
输出格式
一行 \(5\) 个字符,空格分开。分别表示每局先取球的人能否获胜。能获胜则输出 +
,次之,如有办法逼平对手,输出 0
,无论如何都会输,则输出 -
。
数据范围
\(0<n_1,n_2,n_3<100,\)
\(0<x_i<1000\)
输入样例1:
1 2 3
1 2 3 4 5
输出样例1:
+ 0 + 0 -
输入样例2:
1 4 5
10 11 12 13 15
输出样例2:
0 - 0 + +
输入样例3:
2 3 5
7 8 9 10 11
输出样例3:
+ 0 0 0 0
解题思路
博弈论,记忆化搜索,dp
针对先手来说,
- 状态表示:\(f[op][i][j]\) 表示当前轮到先手(\(op=0\))/后手(\(op=1\))操作,且此时先手有 \(i\) 个球,后手有 \(j\) 个球的最佳结果,如果先手能赢,则结果为 \(1\),否则如果能平,则结果为 \(3\),否则为 \(2\)
然后记忆化搜索,轮到先手时,如果存在一个后继状态使得先手能赢,则当前状态能赢,直接返回,否则如果能平,则最后如果没有赢的状态则尽量平;轮到后手,如果存在一种状态使得先手输,则先手输,直接返回,否则如果能平,则最后如果先手没有输的状态则尽量平,如果连平的状态都没有,则先手必胜
- 时间复杂度:\(O(n^2)\)
代码
// %%%Skyqwq
#include <bits/stdc++.h>
#include <tr1/unordered_map>
#include <tr1/unordered_set>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
using namespace tr1;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1005;
int num[4],n,f[2][N][N];
int dfs(int op,int a,int b)
{
if(f[op][a][b])return f[op][a][b];
int mn=min(num[1],min(num[2],num[3]));
int rest=n-a-b;
if(rest<mn)
{
if(a%2==1&&b%2==0)return f[op][a][b]=1;
if(b%2==1&&a%2==0)return f[op][a][b]=2;
return f[op][a][b]=3;
}
bool ff=false;
for(int i=1;i<=3;i++)
{
if(op)
{
if(num[i]>rest)continue;
int res=dfs(0,a,b+num[i]);
if(res==2)return f[op][a][b]=2;
else if(res==3)ff=true;
}
else
{
if(num[i]>rest)continue;
int res=dfs(1,a+num[i],b);
if(res==1)return f[op][a][b]=1;
else if(res==3)ff=true;
}
}
if(!f[op][a][b])
{
if(ff)return f[op][a][b]=3;
else if(op)
return f[op][a][b]=1;
else
return f[op][a][b]=2;
}
}
int main()
{
cin>>num[1]>>num[2]>>num[3];
for(int i=1;i<=5;i++)
{
cin>>n;
memset(f,0,sizeof f);
int res=dfs(0,0,0);
if(res==1)cout<<"+ ";
else if(res==2)cout<<"- ";
else
cout<<"0 ";
}
return 0;
}