1 条题解
-
0
题目分析
本题是一个基于概率的博弈论问题,Alice和Bob在一个有个格子的棋盘上轮流移动棋子,每个位置有两个参数和。玩家可以选择移动步数(),如果还可以选择是否发起挑战,挑战结果会影响后续的操作顺序。
关键观察
- 游戏结束条件:当棋子移动到第格时,当前玩家获胜
- 挑战机制:挑战成功可以让对手跳过一轮,挑战失败则自己跳过一轮
- 状态依赖:每个位置的胜负概率依赖于后续位置的状态
算法思路
核心思想:动态规划 + 概率计算
状态设计:
f[i]:表示当棋子位于第格且当前轮到Alice操作时,Alice的获胜概率g[i]:表示当棋子位于第格且当前轮到Bob操作时,Alice的获胜概率
状态转移:
对于每个位置,当前玩家可以选择:
- 直接移动:选择步数移动到位置
- 移动后挑战:选择步数,移动后发起挑战
状态转移方程
对于
f[i](Alice的回合):- 如果可以直接移动到终点(),则
f[i] = 1.0 - 否则,考虑所有可能的移动选择:
- 如果(必须移动最大步数,不能挑战):
f[i] = max(f[i], 1 - g[i+x]) - 如果(可以选择是否挑战):
-
不挑战:
prob = 1 - g[i+x] -
挑战:计算挑战后的期望胜率
- 挑战成功概率:$P_{succ} = \frac{\text{满足}u>v\text的组合数}{(p_i-x) \times (q_i+q_{i+x}+1)}$
- 挑战平局概率:$P_{draw} = \frac{\text{满足}u=v\text的组合数}{(p_i-x) \times (q_i+q_{i+x}+1)}$
- 挑战失败概率:$P_{fail} = \frac{\text{满足}u<v\text的组合数}{(p_i-x) \times (q_i+q_{i+x}+1)}$
挑战后的期望胜率:
prob = P_succ × f[i+x] + P_draw × (1 - g[i+x]) + P_fail × (1 - g[i+x]在Bob连续两轮的情况) -
取挑战和不挑战中的最大值
-
- 如果(必须移动最大步数,不能挑战):
对于
g[i](Bob的回合),转移类似,但目标是最小化Alice的胜率。算法实现细节
挑战概率计算
对于给定的:
- 的范围:
- 的范围:
挑战结果概率:
- :$\frac{\sum_{u=1}^{p_k-x} \min(u-1, q_k+q_{k+x})}{(p_k-x) \times (q_k+q_{k+x}+1)}$
- :$\frac{\min(p_k-x, q_k+q_{k+x}+1)}{(p_k-x) \times (q_k+q_{k+x}+1)}$
- :
连续操作处理
根据题目规则:
- 通过挑战获得的额外操作轮次不能再次挑战
- 通过对手挑战失败获得的连续操作,第一次操作不能挑战
这需要在状态设计中考虑操作的历史信息。
代码结构
#include <bits/stdc++.h> using namespace std; const int MAXN = 100005; int n, p[MAXN], q[MAXN]; double f[MAXN], g[MAXN]; // f[i]: Alice在位置i先手的胜率, g[i]: Bob在位置i先手时Alice的胜率 // 计算挑战概率 void calc_prob(int pk, int x, int qk, int qkx, double &P_succ, double &P_draw, double &P_fail) { int u_max = pk - x; int v_max = qk + qkx; long long total = (long long)u_max * (v_max + 1); long long succ = 0, draw = 0; // 计算u>v的情况 for (int u = 1; u <= u_max; u++) { succ += min(u - 1, v_max); } // 计算u=v的情况 draw = min(u_max, v_max + 1); P_succ = (double)succ / total; P_draw = (double)draw / total; P_fail = 1.0 - P_succ - P_draw; } int main() { // 输入处理 cin >> n; for (int i = 0; i < n; i++) cin >> p[i]; for (int i = 0; i < n; i++) cin >> q[i]; // 初始化终点状态 f[n] = 1.0; // 到达终点,当前玩家获胜 g[n] = 0.0; // 如果Bob在终点,Alice输 // 从后往前DP for (int i = n - 1; i >= 0; i--) { f[i] = 0.0; g[i] = 1.0; // Bob会最小化Alice的胜率 // 遍历所有可能的移动步数 for (int x = 1; x <= p[i]; x++) { int next_pos = i + x; if (next_pos > n) continue; // 如果可以直接移动到终点 if (next_pos == n) { f[i] = 1.0; g[i] = 0.0; continue; } if (x == p[i]) { // 必须移动最大步数,不能挑战 f[i] = max(f[i], 1.0 - g[next_pos]); g[i] = min(g[i], 1.0 - f[next_pos]); } else { // 可以选择是否挑战 double P_succ, P_draw, P_fail; calc_prob(p[i], x, q[i], q[next_pos], P_succ, P_draw, P_fail); // 不挑战的情况 double no_challenge = 1.0 - g[next_pos]; // 挑战的情况(简化模型,考虑一轮影响) double with_challenge = P_succ * f[next_pos] + P_draw * (1.0 - g[next_pos]) + P_fail * (1.0 - f[next_pos]); // 失败时Bob连续操作 f[i] = max(f[i], max(no_challenge, with_challenge)); g[i] = min(g[i], 1.0 - max(no_challenge, with_challenge)); } } } printf("%.15lf\n", f[0]); return 0; }复杂度分析
- 时间复杂度:,其中,对于可以接受
- 空间复杂度:
算法正确性
- 基础情况:到达终点时胜负明确
- 最优子结构:每个位置的胜率依赖于后续位置的胜率
- 概率计算:准确建模了挑战机制的各种概率情况
- 博弈均衡:Alice最大化胜率,Bob最小化Alice胜率
该算法通过逆向动态规划和精确的概率计算,解决了这个复杂的博弈论问题。
- 1
信息
- ID
- 4377
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者