bzoj 1426 收集邮票 期望dp

题面

题目传送门

解法

有一定思维难度的期望dp

  • 首先,如果直接强行求最后期望花钱的数量是不太可行的
  • 那么,我们不妨先求出期望买的张数,然后就可以比较方便地求出最后的答案
  • f [ i ] 表示已经收集到 i 种不同的邮票,还需要再购买的邮票数的期望值,不妨就假设那 i 种邮票就是前 i 种,因为我们并不关心到底购买到的是哪一种
  • 那么,关于 f [ i ] 的转移方程是比较容易得到的,考虑下一次买到的是什么,有 i n 的概率会买到之前的 i 张,有 n i n 的概率会买到剩余没有被买到过的
  • 所以,我们就可以得到 f [ i ] = i n f [ i ] + n i n f [ i + 1 ] + 1 。把这个式子转化一下,就变成 f [ i ] = f [ i + 1 ] + n n i 。得到这个式子可以根据期望的线性性。
  • 然后,我们再设 g [ i ] 表示已经收集到 i 种不同的邮票,还需要购买的期望代价
  • 可以发现, g 的转移其实和 f 很类似,可以得到 g [ i ] = i n ( g [ i ] + f [ i ] + 1 ) + n i n ( g [ i + 1 ] + f [ i + 1 ] + 1 ) ,稍微移项一下就变成 g [ i ] = i n i f [ i ] + g [ i + 1 ] + f [ i + 1 ] + n n i
  • 那么,我们最后的答案就是 g [ 0 ]
  • 本题主要运用期望的线性性,这个性质是解决期望问题的关键
  • 解决期望问题一般都是倒推
  • 时间复杂度: O ( n )

代码

#include <bits/stdc++.h>
#define double long double
#define N 10010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
double f[N], g[N];
int main() {
    int n; read(n); f[n] = g[n] = 0;
    for (int i = n - 1; ~i; i--) {
        f[i] = f[i + 1] + (double)n / (n - i);
        g[i] = (double)i / (n - i) * f[i] + g[i + 1] + f[i + 1] + (double)n / (n - i);
    }
    cout << fixed << setprecision(2) << g[0] << "\n";
    return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/81979052