马尔可夫【MDP】
Alice 和 Bob 正在玩一个 k 轮的石头剪刀布游戏。游戏开始时两位玩家各有恰好 3 张牌每张牌是石 头R、剪刀S或布P。每一轮游戏按照如下方式进行 1. 双方均可看见彼此手中的全部 6 张手牌。 2. Alice 先从她的手牌中选择一张牌打出。 3. Bob 在看到 Alice 打出的牌后选择自己的一张牌打出。 4. 根据标准规则判定胜负石头胜剪刀剪刀胜布布胜石头。相同则为平局。若 Alice 获胜她得 3 分若平局她得 1 分若 Bob 获胜她得 0 分。 5. 双方都将打出的牌丢弃并各自独立地以等概率获得一张新的石头、剪刀或布。 Alice 希望最大化自己的期望总得分Bob 希望最小化 Alice 的期望总得分。 给定游戏轮数 k、Alice 的初始手牌和 Bob 的初始手牌你需要求出双方都采取最优策略时Alice 的最 大期望总得分。 Input 输入的第一行包含一个整数 T (1 ≤ T ≤ 10 5 )表示测试数据组数。对于每组测试数据 第一行包含一个整数 k (1 ≤ k ≤ 10 9 )表示游戏轮数。 第二行包含一个长度为 3 的字符串由字符 R, S, P 组成表示 Alice 的初始手牌。 第三行包含一个长度为 3 的字符串由字符 R, S, P 组成表示 Bob 的初始手牌。 Output 对于每组测试数据输出一行包含一个实数表示 Alice 的最大期望总得分。 如果你的答案的绝对误差或相对误差不超过 10 −6则将被视为正确。形式化地设你的输出为 a标准 答案为 b当且仅当 |a−b| max(1,|b|) ≤ 10 −6 时你的输出会被接受。一、核心思路1. 状态表示去重石头剪刀布中手牌的顺序不影响结果。例如RSP、PSR、SPR是一样的。为了去重我们将手牌排序。Alice 的手牌排序后是一个非降序三元组例如{0, 0, 2}(R, R, P)。这样的组合只有 10 种数学上叫可重复组合(3C33−1​)10。我们将这 10 种组合编号为 0~9。全局状态Alice的状态 10 * Bob的状态。范围 0~99。2. 动态规划定义dp[i][zt]表示还剩下i轮游戏当前状态为zt时Alice 能获得的期望总得分。3. 博弈过程模拟DP转移对于状态zt还剩i轮Alice 出牌她手上有 3 张牌她尝试出第a张位置 0, 1, 2。Bob 出牌他知道 Alice 出了什么。他遍历自己手上的 3 张牌选择一张让 Alice得分最少的牌。弃牌与补牌双方各弃一张然后各自独立随机摸一张R/S/P。这里有 3×39种可能性。状态转移摸牌后手牌变化重新排序进入新状态zt剩余i-1轮。得分计算本轮得分 下一轮期望得分。由于 Bob 是后手且想最小化 Alice 得分内层取minAlice 想最大化得分外层取max。4. 处理巨大的 k线性外推k最大到 10e9不可能算这么多轮。观察发现当 i很大时每多一轮游戏期望得分的增加量趋于稳定就像物理中的匀速运动。我们计算前 1000 轮。计算平均每轮增加的分值 d通过计算第 1000 轮与第 999 轮的差值平均得到。对于 k1000使用公式dp[1000][zt](k−1000)×d。二、带详细注释的代码#include bits/stdc.h using namespace std; #define ll long long #define ull unsigned long long #define ld long double // 使用长双精度防止精度丢失 #define endl \n // 计时器用于调试性能 chrono::_V2::system_clock::time_point bg_clock,en_clock; const int N 1e3 10; // DP数组第一维大小预计算1000轮 ld dp[N][100]; // dp[i][zt]: 剩余i轮状态为zt时的期望得分 // 存储所有10种手牌组合排序后的 std::vectorarrayll, 3 card; // 映射手牌组合 - 索引 (0~9) maparrayll, 3, ll mp; ld d 0; // 稳态下平均每轮的得分增量 /** * brief 判断对局得分 * param a Alice出的牌 (0:R, 1:S, 2:P) * param b Bob出的牌 * return Alice的得分 (0, 1, or 3) */ ll check(int a, int b) { if (a b) { return 1; // 平局 } // (01)%31 (R beats S) // (11)%32 (S beats P) // (21)%30 (P beats R) if ((a 1) % 3 b) { return 3; // Alice赢 } return 0; // Alice输 } /** * brief 初始化函数生成状态空间并计算DP表 */ void init() { ll cnt 0; // 1. 生成所有非降序三元组共10种 // 例如 {0,0,0}, {0,0,1}, {0,1,1}...{2,2,2} for (int i 0; i 3; i) { for (int j i; j 3; j) { for (int k j; k 3; k) { card.push_back({i, j, k}); mp[{i, j, k}] cnt; cnt; } } } // 2. 动态规划计算 // i 代表剩余的轮数 for (int i 1; i N; i) { // 遍历所有100个状态 (Alice 10种 * Bob 10种) for (int zt 0; zt 100; zt) { ld resa -1e18; // Alice能获得的最大期望得分 // Alice尝试打出她手上的第a张牌 (0, 1, or 2) for (int a 0; a 3; a) { ld resb 1e18; // Bob操作后的结果他要最小化这个值 // Bob尝试打出他手上的第b张牌 for (int b 0; b 3; b) { ld sum 0; // 枚举双方随机补充的牌 (na: new Alice, nb: new Bob) for (int na 0; na 3; na) { for (int nb 0; nb 3; nb) { // 解析当前状态 zt // zt % 10 是 Alice 的状态索引, zt / 10 是 Bob 的状态索引 ll za zt % 10; ll zb zt / 10; // 获取当前手牌注意这里是拷贝因为要修改 auto cuna card[za]; // Alice current hand auto cunb card[zb]; // Bob current hand // Alice打出第a张牌并用新牌na填充该位置 cuna[a] na; // Bob打出第b张牌并用新牌nb填充该位置 cunb[b] nb; // 手牌重新排序因为顺序不影响后续状态 sort(cuna.begin(), cuna.end()); sort(cunb.begin(), cunb.end()); // 累加下一轮的期望得分 本轮即时得分 // dp[i-1][...]: 剩下 i-1 轮的期望 // check(...): 本轮得分 sum dp[i - 1][mp[cuna] 10 * mp[cunb]] check(card[za][a], card[zb][b]); } } // Bob选择让他付出代价最小的出牌即最小化sum resb min(resb, sum); } // Alice选择让她获益最大的出牌 // 注意resb 是9种情况的总和除以9才是期望值 resa max(resa, resb / 9.0); } dp[i][zt] resa; } } // 3. 计算稳态平均每轮收益 d // 假设当轮数很大时dp[i] - dp[i-1] 趋近于常数 d for (int i 0; i 100; i) { // 取第1000轮和第999轮的差值作为增量估计 d (dp[1000][i] - dp[999][i]); } d / 100; // 对所有100个状态取平均得到稳定的d } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); bg_clock chrono::high_resolution_clock::now(); cout fixed setprecision(15); // 设置高精度输出 int T 1; cin T; init(); // 一次性预处理所有数据 while (T--) { ll k; cin k; arrayll, 3 ca, cb; // current Alice/Bob hand string sa, sb; cin sa sb; // 将输入的字符转换为数字 (0:R, 1:S, 2:P) for (int i 0; i 3; i) { if (sa[i] R) ca[i] 0; if (sa[i] S) ca[i] 1; if (sa[i] P) ca[i] 2; if (sb[i] R) cb[i] 0; if (sb[i] S) cb[i] 1; if (sb[i] P) cb[i] 2; } // 排序匹配状态定义 sort(ca.begin(), ca.end()); sort(cb.begin(), cb.end()); // 计算当前状态编码 ll zt mp[ca] 10 * mp[cb]; if (k N) { // 如果k小于预计算的轮数直接查表 cout dp[k][zt] endl; } else { // 否则利用线性外推公式计算 // dp[1000][zt]: 前1000轮的得分 // (k-1000)*d: 剩余轮数按平均速度获得的得分 cout dp[1000][zt] (k - 1000) * d endl; } } en_clock chrono::high_resolution_clock::now(); auto duration_clock chrono::duration_castchrono::microseconds(en_clock - bg_clock); ld duration_count duration_clock.count() * 0.001; // cerr Time: duration_count ms endl; // 输出运行时间调试用 return 0; }三、这段代码为什么能过精度控制使用了long double和setprecision(15)远超题目要求的1e-6。状态压缩100个状态使得DP可行。外推合理性由于每轮结束后的随机补牌状态转移是无记忆的马尔可夫性。当轮数趋于无穷平均收益确实会收敛到一个固定值。用第1000轮的数据外推误差极小。博弈逻辑正确严格遵循了“Alice最大化Bob最小化”的零和博弈规则。原理讲解一、马尔可夫性一句话定义给定一个过程如果下一时刻的状态只取决于当前状态跟更早的历史无关这个过程就满足马尔可夫性。形式化写P(St1​∣St​,St−1​,…,S0​)P(St1​∣St​)回到这题当前状态 (Alice手牌, Bob手牌)下一轮手牌只由当前手牌 这轮谁出啥 随机补牌决定前 5 轮是怎么打到这个状态的完全不影响下一轮所以这是个马尔可夫过程。状态只有 100 个就叫有限状态马尔可夫链。二、马尔可夫链的几个性格一条链长什么样主要看三个属性不可约Irreducible从任意一个状态出发迟早能走到任意另一个状态。这题双方每轮都要弃牌随机补牌哪怕现在 Alice 全是 R、Bob 全是 P几轮之内总能变到任何其他组合因为补牌是独立的总有概率摸到想要的。则这条链是不可约的。非周期Aperiodic不存在一个强制的每隔几轮才回来的规律。这题有可能下一轮就回到同一个手牌组合运气好补回来一样的也可能隔几轮才回来。周期可以是 1。这条链是非周期的。遍历Ergodic 不可约 非周期 有限状态三条都占 →你这条链是遍历的。遍历——保证下面所有稳定的结论都成立。三、平稳分布 π最关键的结论遍历链有一个超级重要的定理存在唯一的分布 π(π0​,π1​,…,π99​)满足πPπ并且不管从哪个状态出发玩很多轮之后状态分布都会收敛到 π跟起点无关。π叫平稳分布平稳的意思是如果一开始按 π发手牌那之后每一轮的手牌分布都永远是 π。四、平均报酬定理代码外推题每轮有个即时奖励​ ri​在状态 i下 Alice 这轮的期望得分Bob 已经最优应对了。遍历链 常驻奖励有个定理叫平均报酬定理k→∞lim​kVk​(目i)​ρ:j0∑99​πj​rj​ρ就是长期平均每轮得分是个常数跟起点 i无关。更精细一点值函数可以拆成Vk​(i)kρhi​o(1)其中 hi​叫偏差bias只跟起点有关是常数。o(1)那项是残余随 k指数衰减。五、把代码里的变量对号入座数学符号你代码里的东西含义S100dp[][100]第二维状态数Pji​9 种补牌等概率转移概率ri​check(...) 下一轮dp里的即时部分单轮奖励π--平稳分布ρd平均每轮得分hi​dp[1000][i] - 1000*d的隐式估计偏差d (dp[1000][i] - dp[999][i]); // 对所有i平均 d / 100;数学上dp[1000][i]−dp[999][i]ρ(hi​−hi​)残留≈ρ再对所有 i平均一把残留更小 →用数值差分估 ρ。然后外推dp[1000][zt] (k-1000)*d // 1000*ρ h_zt 残 (k-1000)*ρ // k*ρ h_zt 残正好是 Vk​(zt)的近似。六、这套知识在竞赛里还用在哪马尔可夫链 平稳分布这套在算法竞赛里是一类题的通解题型例子随机游走求长期比例蚂蚁在图上爬问某条边被走的概率期望 DP 巨大轮数这题就是吸收态问题gamblers ruin、毒药糖果随机过程博弈Bob 后手通用套路建模状态证明/确认是马尔可夫链判遍历性​ → 有平稳分布 π小 k 直接 DP大 k要么像本题这样数值外推好写够用要么解 πPπ 得到精确 ρ再配偏差 h更稳适合 k 到 1e18 的题七、这题如果用正统 MDP 解法现在的代码 数值迭代 线性外推是工程师解法。正统 MDP / 马尔可夫报酬链解法会多两步策略迭代或值迭代得到最优策略下固定的转移 P∗和奖励 r∗解πP∗π→ 平稳分布ρπr∗(I−P∗1π)hr∗−ρ1→ 偏差答案 kρhi​−(P∗kh)i​大 k 时最后一项丢掉好处ρ 和 h 都是解析精确值不用赌1000 轮够不够稳。

相关新闻

从AI乱编到出版级输出:一位童书编辑+AI训练师双身份者的12小时实战复盘——如何用ChatGPT批量产出获凯迪克奖风格故事脚本

从AI乱编到出版级输出:一位童书编辑+AI训练师双身份者的12小时实战复盘——如何用ChatGPT批量产出获凯迪克奖风格故事脚本

更多请点击: https://intelliparadigm.com 第一章:从AI幻觉到出版级叙事的范式跃迁 当大语言模型生成“华盛顿特区位于加拿大”这类事实性错误时,我们遭遇的并非偶然失误,而是底层概率建模与真实世界语义锚定之间的结构性断裂。出…

2026/9/30 13:40:10 阅读更多 →
紧急通知:网信办新规实施倒计时72小时!你的AI评论系统是否通过“三审一校”兼容性验证?

紧急通知:网信办新规实施倒计时72小时!你的AI评论系统是否通过“三审一校”兼容性验证?

更多请点击: https://intelliparadigm.com 第一章:紧急通知:网信办新规实施倒计时72小时!你的AI评论系统是否通过“三审一校”兼容性验证? 距离《生成式人工智能服务安全评估办法》配套实施细则正式生效仅剩72小时。网…

2026/9/29 22:01:04 阅读更多 →
教务排课中的多约束冲突解决:当40条特殊需求同时砸过来,算法如何破局?

教务排课中的多约束冲突解决:当40条特殊需求同时砸过来,算法如何破局?

排课不是简单的表格填空,而是一场约束满足问题与人性博弈的复合战役。本文从真实教务场景出发,探讨多优先级约束冲突下的排课优化思路。一、问题缘起:一张课表背后的人性博弈 如果你问一位教务老师,排课最难的是什么?答…

2026/9/26 8:38:56 阅读更多 →

最新新闻

AI工程化实战:从零构建可交付、可运维的AI系统

AI工程化实战:从零构建可交付、可运维的AI系统

1. 这不是“搭积木”,而是亲手锻造AI系统的完整工程实践“AI Engineering from Scratch”——看到这个标题,很多人第一反应是:又要从零写Transformer?还是手推反向传播公式?其实完全不是。我带过六支AI产品团队&#x…

2026/9/30 13:40:59 阅读更多 →
GitHub日榜挖掘指南:从热门仓库到技术选型能力

GitHub日榜挖掘指南:从热门仓库到技术选型能力

1. 先搞懂:GitHub 日榜到底是什么,它值得每天看吗每个工作日晚上,只要手头没有紧急上线任务,我都会把 GitHub 首页右上角的 Trending 页面刷一遍,尤其是“Today”这个 Tab。在别人眼里这可能只是又一个“开发者热搜榜”…

2026/9/30 13:40:59 阅读更多 →
计算机体系结构核心考点:从CPU性能公式到流水线与Cache

计算机体系结构核心考点:从CPU性能公式到流水线与Cache

计算机体系结构这门课,很多人第一反应是“背知识点”,第二反应是“算CPI”。我在山大把这门课完整啃下来之后发现,它其实是整个计算机专业里最讲“权衡”的一门课——没有绝对的最优,只有针对某个目标的取舍。这篇知识点清单就是我…

2026/9/30 13:40:59 阅读更多 →
酒店三网分离设计:VLAN划分、DHCP隔离与PoE供电实战指南

酒店三网分离设计:VLAN划分、DHCP隔离与PoE供电实战指南

简介:这是一份酒店项目网络系统设计方案文档,面向弱电智能化设计师、网络工程师及售前方案人员,适合用于酒店类项目投标、方案编写或毕业设计参考。内容以酒店客用网、管理办公网与智能化专网三套独立网络为主线,完整说明二层千兆…

2026/9/30 13:40:59 阅读更多 →
GB/T 2423系列环境试验标准全梳理:版本对照与受控文件管理实战

GB/T 2423系列环境试验标准全梳理:版本对照与受控文件管理实战

前阵子做内部审核,检测中心被审查员提了一个让我很惭愧的问题:同一款电源产品做低温试验,研发图纸写的是“按GB/T 2423.1”,委托单上写的是“2423.1-2001”,实验室自己的作业指导书又按2008版执行。三种表述放在一起&a…

2026/9/30 13:40:59 阅读更多 →
Spring Boot实战:基于OpenAI API构建流式AI对话服务的工程化落地指南

Spring Boot实战:基于OpenAI API构建流式AI对话服务的工程化落地指南

做后端这些年,我越来越觉得“把大模型能力接进业务系统”这件事,真正的难点从来不在调 API 本身,而在工程化落地。去年我在一个电商项目里用 Spring Boot 封装 OpenAI API 搭 AI 对话服务,前后踩了不少坑:有流式输出调…

2026/9/30 13:39:58 阅读更多 →

日新闻

Base64 图片头部特征识别:从文件头到格式判断的完整指南

Base64 图片头部特征识别:从文件头到格式判断的完整指南

1. 项目概述:为什么说看懂 base64 图片头部是基本功这几年跟 base64 打交道的机会越来越多,后端接口返回图片、前端渲染验证码、小程序里存小图、还有一些老系统导出报表,动不动就给你一段长到怀疑人生的 base64 字符串。很多人拿到字符串就直…

2026/9/30 0:00:35 阅读更多 →
Java公交站牌广告管理系统:JSP+Servlet+MySQL实战落地指南

Java公交站牌广告管理系统:JSP+Servlet+MySQL实战落地指南

简介:本资源是一份面向Java初学者与课程设计学生的公交站牌广告灯箱管理系统毕业设计文档,聚焦城市公共广告资源信息化管理痛点,提供从需求分析到技术实现的完整方案。文档采用标准学术论文结构,含摘要、英文摘要、目录及五章正文…

2026/9/30 0:00:35 阅读更多 →
用 Redis Lua 构建大模型 API 多租户原子配额治理体系

用 Redis Lua 构建大模型 API 多租户原子配额治理体系

我去年年底接了一个内部 AI 平台的治理需求,背景很直接:公司把 DeepSeek、MiniMax 这类大模型 API 统一封装成内部网关,开放给几个业务团队用。结果第一个月账单出来,额度直接超了 4 倍。仔细查日志,发现原因并不复杂—…

2026/9/30 0:00:35 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/30 13:14:22 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 16:41:41 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/30 13:14:49 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/29 19:29:29 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/29 5:58:00 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/29 3:55:56 阅读更多 →