蓝桥杯 取球博弈

取球博弈

原题目链接

题目描述

两个人玩取球的游戏。

一共有 N 个球,每人轮流取球,每次可取集合 n₁, n₂, n₃ 中的任何一个数目。

如果无法继续取球,则游戏结束。

此时,持有奇数个球的一方获胜。

如果两人都是奇数,则为平局。

假设双方都采用最聪明的取法,第一个取球的人一定能赢吗?

试编程解决这个问题。


输入描述

输入格式:

第一行 3 个正整数 n₁, n₂, n₃

(0 < n₁, n₂, n₃ < 100),空格分开,表示每次可取的数目。

第二行 5 个正整数 x₁, x₂, ⋯, x₅

(0 < xᵢ < 1000),空格分开,表示 5 局的初始球数。


输出描述

输出一行 5 个字符,空格分开。

分别表示每局先取球的人能否获胜。

  • 能获胜则输出 +
  • 如有办法逼平对手,输出 0
  • 无论如何都会输,则输出 -

输入输出样例

输入

in 复制代码
1 2 3
1 2 3 4 5

输出

out 复制代码
+ 0 + 0 -

c++代码

cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

unordered_map<string, string> mp;
vector<int> arr(3);

int x, n1, n2, n3;

string dfs(int a, int b) {
    string str = to_string(x) + " " + to_string(a % 2) + " " + to_string(b % 2);
    if (mp.find(str) != mp.end()) return mp[str];
    if (x < arr[0]) {
        if (a % 2 != 0 && b % 2 == 0) {mp[str] = "+"; return "+";}
        else if (a % 2 == 0 && b % 2 != 0) {mp[str] = "-"; return "-";}
        else return "0";
    }
    string sym = "-";
    for (int i = 0; i < 3; i++) {
        if (x < arr[i]) break;
        x -= arr[i];
        a += arr[i];
        string op = dfs(b, a);
        x += arr[i];
        a -= arr[i];
        if (op == "-") {mp[str] = "+"; return "+";}
        else if (op == "0") sym = "0";
    }
    mp[str] = sym;
    return sym;
}

int main() {
    for (int i = 0; i < 3; i++) cin >> arr[i];
    sort(arr.begin(), arr.end());
    for (int i = 0; i < 5; i++) {
        cin >> x;
        if (i != 4) cout << dfs(0, 0) << " ";
        else cout << dfs(0, 0) << endl;
    }
    return 0;
}

题解

博弈论(递归+回溯)

定义string dfs(int a, int b)的返回值为剩余球数n,先取秋的人的当前球数a,后取球的人的当前球数b的局面情况。

如果当前剩余球数x小于n1,n2,n3的最小值,则无法继续取球,游戏结束。结算胜负。

cpp 复制代码
if (x < arr[0]) {
    //我对arr排序了,所以第一个就是最小的。
    if (a % 2 != 0 && b % 2 == 0) return "+";
    else if (a % 2 == 0 && b % 2 != 0) return "-";
    else return "0";
}

我们枚举先取球的人可以取出的球数

cpp 复制代码
x -= arr[i];
a += arr[i];

然后我们调用dfs(b, a)得出敌方的局面。

如果敌方必输,那么我们必赢。

如果敌方必赢,也不能说我们必输,因为我们可以不取这个数量的球,可以换个数量试试。

但是如果不管我们取什么数量的球,敌方的局面都是必赢,那么我们才是必输。

如果敌方平局,且敌方没有必输的情况,那么我们最好的局面也是平局。

cpp 复制代码
string sym = "-";
for (int i = 0; i < 3; i++) {
    if (x < arr[i]) break;
    x -= arr[i];
    a += arr[i];
    string op = dfs(b, a);
    x += arr[i];
    a -= arr[i];
    if (op == "-") return "+"; //如果敌方必输,那么我们必赢。
    else if (op == "0") sym = "0";//如果敌方平局,且敌方没有必输的情况,那么我们最好的局面也是平局。
}
return sym;//不管我们取什么数量的球,敌方的局面都是必赢,那么我们才是必输。

记忆化搜索

一局的胜负由,剩余球数n,先取秋的人的当前球数a的奇偶性,后取球的人的当前球数b的奇偶性共同决定。

我们用字符串str存储这种情况,用mp存储这种情况的局面。

cpp 复制代码
string str = to_string(x) + " " + to_string(a % 2) + " " + to_string(b % 2);
unordered_map<string, string> mp;

当我们再次碰到相同的局面我们直接返回存储好的值,而不是重复同样的dfs(a, b),省下很多时间。

cpp 复制代码
if (mp.find(str) != mp.end()) return mp[str];
相关推荐
多米Domi0118 分钟前
0x3f 第25天 黑马web (145-167)hot100链表
数据结构·python·算法·leetcode·链表
LYFlied8 分钟前
【每日算法】LeetCode 207. 课程表
算法·leetcode·职场和发展
sali-tec10 分钟前
C# 基于OpenCv的视觉工作流-章7-膨胀
图像处理·人工智能·opencv·算法·计算机视觉
叫我:松哥13 分钟前
基于机器学习的地震风险评估与可视化系统,采用Flask后端与Bootstrap前端,系统集成DBSCAN空间聚类算法与随机森林算法
前端·算法·机器学习·flask·bootstrap·echarts·聚类
一起养小猫15 分钟前
LeetCode100天Day12-删除重复项与删除重复项II
java·数据结构·算法·leetcode
码农丁丁18 分钟前
谈谈面试的本质
面试·职场和发展·技术管理·ai时代的技术管理
小O的算法实验室21 分钟前
2023年IEEE TITS SCI2区TOP,增强遗传算法+分布式随机多无人机协同区域搜索路径规划,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
Allen_LVyingbo28 分钟前
病历生成与质控编码的工程化范式研究:从模型驱动到系统治理的范式转变
前端·javascript·算法·前端框架·知识图谱·健康医疗·easyui
一起努力啊~28 分钟前
算法刷题--螺旋矩阵II+区间和+开发商购买土地
数据结构·算法·leetcode
Swift社区29 分钟前
LeetCode 470 用 Rand7() 实现 Rand10()
算法·leetcode·职场和发展