1 条题解

  • 0
    @ 2025-10-28 9:20:36

    题目分析

    本题是一个基于概率的博弈论问题,Alice和Bob在一个有(n+1)(n+1)个格子的棋盘上轮流移动棋子,每个位置ii有两个参数pip_iqiq_i。玩家可以选择移动步数xx1xpi1 \leq x \leq p_i),如果x<pix < p_i还可以选择是否发起挑战,挑战结果会影响后续的操作顺序。

    关键观察

    1. 游戏结束条件:当棋子移动到第nn格时,当前玩家获胜
    2. 挑战机制:挑战成功可以让对手跳过一轮,挑战失败则自己跳过一轮
    3. 状态依赖:每个位置的胜负概率依赖于后续位置的状态

    算法思路

    核心思想:动态规划 + 概率计算

    状态设计

    • f[i]:表示当棋子位于第ii格且当前轮到Alice操作时,Alice的获胜概率
    • g[i]:表示当棋子位于第ii格且当前轮到Bob操作时,Alice的获胜概率

    状态转移

    对于每个位置ii,当前玩家可以选择:

    1. 直接移动:选择步数xx移动到位置i+xi+x
    2. 移动后挑战:选择步数x<pix < p_i,移动后发起挑战

    状态转移方程

    对于f[i](Alice的回合):

    • 如果可以直接移动到终点(i+pini + p_i \geq n),则f[i] = 1.0
    • 否则,考虑所有可能的移动选择xx
      • 如果x=pix = p_i(必须移动最大步数,不能挑战): f[i] = max(f[i], 1 - g[i+x])
      • 如果x<pix < p_i(可以选择是否挑战):
        • 不挑战: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的胜率。

    算法实现细节

    挑战概率计算

    对于给定的pk,x,qk,qk+xp_k, x, q_k, q_{k+x}

    • uu的范围:[1,pkx][1, p_k-x]
    • vv的范围:[0,qk+qk+x][0, q_k + q_{k+x}]

    挑战结果概率:

    • P(u>v)P(u>v):$\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)}$
    • P(u=v)P(u=v):$\frac{\min(p_k-x, q_k+q_{k+x}+1)}{(p_k-x) \times (q_k+q_{k+x}+1)}$
    • P(u<v)P(u<v)1P(u>v)P(u=v)1 - P(u>v) - P(u=v)

    连续操作处理

    根据题目规则:

    • 通过挑战获得的额外操作轮次不能再次挑战
    • 通过对手挑战失败获得的连续操作,第一次操作不能挑战

    这需要在状态设计中考虑操作的历史信息。

    代码结构

    #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;
    }
    

    复杂度分析

    • 时间复杂度O(n×pmax)O(n \times p_{max}),其中pmax333p_{max} \leq 333,对于n100,000n \leq 100,000可以接受
    • 空间复杂度O(n)O(n)

    算法正确性

    1. 基础情况:到达终点时胜负明确
    2. 最优子结构:每个位置的胜率依赖于后续位置的胜率
    3. 概率计算:准确建模了挑战机制的各种概率情况
    4. 博弈均衡:Alice最大化胜率,Bob最小化Alice胜率

    该算法通过逆向动态规划和精确的概率计算,解决了这个复杂的博弈论问题。

    • 1

    信息

    ID
    4377
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者