CF1874D Jellyfish and Miku 记录

题目链接:https://codeforces.com/problemset/problem/1874/D

题意简述

有一个 \(n + 1\) 个点的无向图(\(0 - n\)),第 \(i\) 条边连接点 \(i-1\)\(i\),带有正整数边权 \(a_i\)。在这样的一个图上从点 \(0\) 开始随机游走,点 \(n\) 为吸收点,其余点以边权为概率权重选择连接的一条边。

在所有总和不超过 \(m\) 的边权分配中,求出最短的期望吸收时间。

\(1 \le n \le m \le 3000\)

题解(官解)

首先计算对一个已知的边权分配 \(a_i\) ,从点 \(0\) 开始的期望吸收时间。这是一个经典的概率论问题,记从点 \(i\) 开始的期望吸收时间为 \(E(i)\),则可以列出如下方程:

\[\begin{aligned} & E(n) = 0 \\ & E(i) = 1 + \dfrac 1 {a_i + a_{i+1}} (a_i \cdot E(i-1)+ a_{i+1} \cdot E(i+1)) &(0 < i < n) \\ & E(0) = 1 + E(1) \end{aligned} \]

\(D(i) = E(i - 1) - E(i)\),则可以化简方程如下:

\[\begin{aligned} & 1 + D(i) = \dfrac{a_{i+1}} {a_i} (D(i + 1) - 1) & (0 < i < n)\\ & D(1) = 1 \end{aligned} \]

由数学归纳法可证 \(D(i) = 1 + 2 \sum\limits_{j = 1}^{i-1} \dfrac{a_j}{a_i} = 1 + 2 \dfrac{s_{i-1}}{a_i}\)。则有:

\[E(0) = E(n) + \sum\limits_{i = 1}^n D(i) = \sum\limits_{i = 1}^n (1 + 2 \dfrac{s_{i-1}}{a_i}) = n + 2 \sum_{i =1} ^ n \dfrac{s_{i-1}}{a_i} \]

根据该式,可以使用 DP 求解问题。记 \(dp_{i, x}\) 为当 \(s_i = x\)\(\sum\limits_{j =1} ^ i \dfrac{s_{j-1}}{a_j}\) 的最小值,每次枚举 \(a_i\) 更新,即得到 \(O(nm^2)\) 的解法。

如何优化这个解法?观察式子 \(\sum\limits_{i =1} ^ n \sum\limits_{j = 1}^{i-1} \dfrac{a_j}{a_i}\),若存在 \(a_i > a_{i+1}\),则交换这相邻的两项,式子一定变得更小,因此 \(a\) 是非递减序列,\(a_i \le \dfrac{m}{n - i + 1}\)

根据调和级数相关结论以及 \(n = O(m)\),代码的时间复杂度为 \(O(m^2 \log m)\)

代码实现(C++)

void solve() {
    int n, m;
    cin >> n >> m;
    vector dp(n + 1, vector<double>(m + 1, INFINITY));
    dp[0][0] = 0;
    for (int i = 0; i < n; i++) {
        for (int x = 0; x <= m; x++) {
            for (int y = 1; x + y * (n - i) <= m; y++) {
                dp[i + 1][x + y] = min(dp[i + 1][x + y], dp[i][x] + x * 1.0 / y);
            }
        }
    }
    cout << fixed << setprecision(15) << dp[n][m] * 2 + n << "\n";
}
posted @ 2023-12-17 21:20  cccpchenpi  阅读(41)  评论(0)    收藏  举报