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;
}
posted @ 2022-04-01 11:07  zyy2001  阅读(110)  评论(0编辑  收藏  举报