[ 题解 ] [ HNOI2015 ] [LuoguP3239] 亚瑟王 ( 概率dp )

HNOI2015 亚瑟王

显示/隐藏 题面

题解

概率 dp

首先,设第 \(i\) 卡牌发动技能的概率为 \(f_i\),所有卡牌造成的总伤害的期望为 \(E_d\)

那么,这张卡牌造成的伤害即为 \(f_i d_i\)。(\(d\) 为原题中的伤害)

由于数学期望的线性性质[1] \(E(X + Y) = E(X) + E(Y)\),可知:

\[E_d = \sum_{i=1}^n f_i d_i \]

那么,这题的目标即为求 \(f_i\)

先考虑 \(f_1\)

显然,\(1 - p_1\)\(1\) 号卡牌不发动的概率,因为

  1. 如果这张卡牌在这一局游戏中已经发动过技能,则
    1.1. 如果这张卡牌不是最后一张,则跳过之(考虑下一张卡牌); 否则(是最后一张),结束这一轮游戏。

所以 \(1\) 号牌一直不出的概率为 \((1-p_1)^r\)。(\(r\) 为原题中游戏的轮数)

说明 \(1\) 号牌发动的概率 \(f_1 = 1 - (1 - p_1)^r\)

再考虑 \(f_2\)

分类讨论:

  1. 假如 \(1\) 号卡牌发动了技能

    \[f_2 = 1 - (1 - p_2)^{r - 1} \]

    因为 \(1\) 号卡牌发动了技能,那么:

    1. 否则(这张卡牌在这一局游戏中没有发动过技能),设这张卡牌为第 \(i\)
      2.1. 将其以 \(p_i\) 的概率发动技能。
      2.2 如果技能发动,则对敌方造成 \(d_i\) 点伤害,并结束这一轮。

    第一张牌发动了技能,根据 2.2 结束了这一轮,即与第二张牌发动的概率无关,所以这次为发动的概率不算在 \(f_2\) 中,所以得到指数为 \(r - 1\)

  2. 假如 \(1\) 号卡牌没有发动技能

    \(f_1\) 的情况,

    \[f_2 = 1 - (1 - p_2)^r \]

再考虑接下来的 \(f_i\)

假设在第 \(i\) 张牌尝试发动前已有 \(j\) 张牌成功发动了技能,那么:

\[f_i = 1 - (1 - p_i)^{r-j} \]

考虑到数据较小,

\(1 \leq T \leq 444, 1 \leq n \leq 220, 0 \leq r \leq 132, 0 < p_i < 1, 0 \leq d_i \leq 1000\)

要处理 \(f_i\) ,可以用动态规划。

\(\operatorname{dp}_{i,j}\) 表示前 \(i\) 张牌中,有 \(j\) 张发动了技能的概率。

分类讨论:

  1. \(i\) 张牌发动了技能

    1. 因为有牌发动了技能,所以要保证 \(j > 0\)

    2. 求状态转移方程

      不难看出,前 \(i\) 张牌中,有 \(j\) 张发动了技能的概率,应为前 \(i - 1\) 张牌中,有 \(j - 1\) 张发动了技能的概率乘上第 \(i\) 张牌发动的概率。

      由前文推导到的 \(\small{f_i = 1 - (1 - p_i)^{r-j}}\) 可知第 \(i\) 张牌不发动的概率应该是 \(\small{f_i = 1 - (1 - p_i)^{r-j}}\) 但已经确定这张牌会发动,所以其概率实际为 \(f_i = 1 - (1 - p_i)^{r-j}\)

      得到转移方程 ①:

      \[\operatorname{dp}_{i,j} = \operatorname{dp}_{i-1,j-1} \cdot (1 - (1 - p_i)^{r - j + 1})\quad(j > 0) \]

  2. \(i\) 张牌没有发动技能

    1. 因为第 \(i\) 张牌没有发动技能,所以 \(j \leq i - 1 \iff i \neq j\)

    2. 求状态转移方程

      不难看出,前 \(i\) 张牌中,有 \(j\) 张发动了技能的概率,应为前 \(i - 1\) 张牌中,有 \(j\) 张发动了技能的概率乘上第 \(i\) 张牌不发动的概率。

      \(i\) 张牌不发动的概率就为 \((1 - p_i) ^ {r - j}\)

      得出状态转移方程 ②:

      \[\operatorname{dp}_{i,j} = \operatorname{dp}_{i-1,j} \cdot (1 - p_i)^{r - j}\quad(i \neq j) \]

用动态规划即可求出 \(\operatorname{dp}\)

求出了 \(\operatorname{dp}\),要求 \(f\)

显然,发动第 \(i\) 张牌的概率为前 \(i-1\) 张牌中,有 \(j\) 张发动了技能的概率乘上第 \(i\) 张牌发动的概率的和。(\(j\in\{x \mid 0 \leq x \leq i - 1, x\in\mathbb{Z} \}\)

\[f_i = \sum_{j=0}^{i -1} \operatorname{dp}_{i - 1, j} \cdot (1 - (1 - p_i)^{r - j}) \]

求出了 \(f\),即可求出 \(E_d\)

代码

#include <iostream>
#include <cstring>
#include <iomanip>
#include <cmath>

const int MAX_N = 4e2;

double p[MAX_N];
int d[MAX_N];

double dp[MAX_N][MAX_N];
double f[MAX_N];

double Pw[MAX_N][MAX_N];

int main()
{
    int T;
    std::cin >> T;

    for (int t = 0; t < T; t++)
    {
        // Init
        std::memset(dp, 0, sizeof(dp));
        std::memset(f, 0, sizeof(f));

        int n, r;
        std::cin >> n >> r;

        for (int i = 1; i <= n; i++)
            std::cin >> p[i] >> d[i];

        // Init
        dp[1][0] = std::pow(1 - p[1], r);
        dp[1][1] = 1 - dp[1][0];
        f[1] = dp[1][1];

        // Get dp
        for (int i = 2; i <= n; i++)
        {
            for (int j = 0; j <= std::min(i, r); j++)
            {
                if (j > 0)
                    // f[i][j] = f[i - 1][j - 1] * (1 - (1 - p[i]) ^ (r - j + 1))  (j != 0) ==> do damage from i
                    dp[i][j] += dp[i - 1][j - 1] * (1 - std::pow(1 - p[i], r - j + 1));
                if (i != j)
                    // f[i][j] = f[i - 1][j] * (1 - p[i]) ^ (r - j)  (i != j) ==> no damage from i
                    dp[i][j] += dp[i - 1][j] * std::pow(1 - p[i], r - j);
            }
        }

        // Get f
        for (int i = 2; i <= n; i++)
            for (int j = 0; j <= std::min(i - 1, r); j++)
                f[i] += dp[i - 1][j] * (1 - std::pow(1 - p[i], r - j));

        // Get ans
        double ans = 0;
        for (int i = 1; i <= n; i++)
            ans += f[i] * d[i];

        std::cout << std::fixed << std::setprecision(10) << ans << "\n";
    }

    return 0;
}

参考


  1. 如何理解数学期望的线性性质? ↩︎

posted @ 2021-01-28 10:45  ZTL-UwU  阅读(113)  评论(0编辑  收藏  举报