洛谷 P4550 收集邮票

题目链接:洛谷 P4550 收集邮票

算法标签: 动态规划(DP)期望DP

题目

题目描述

有n种不同的邮票,皮皮想收集所有种类的邮票。唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n。但是由于凡凡也很喜欢邮票,所以皮皮购买第k张邮票需要支付k元钱。
现在皮皮手中没有邮票,皮皮想知道自己得到所有种类的邮票需要花费的钱数目的期望。

输入格式

一行,一个数字N
N<=10000

输出格式

要付出多少钱.
保留二位小数

输入输出样例

输入 #1

3

输出 #1

21.25

题解:

期望DP

按照题目的描述,我们可以设一个\(f[]\)和一个\(g[]\)

\(f[i]\)表示买到第\(i\)张邮票还需要购买的期望次数

\(g[i]\)表示买到第\(i\)张邮票还需要期望花费的钱数。

讲到这里大概就可以发现,这道题需要逆推

对于一个\(f[i]\),我们也可以有以下的几种方案:

​ 会买到重复邮票的概率为\(\frac{i}{n}\),所以这项贡献:\(f[i] \times \frac{i}{n}\)

​ 会买到不同邮票的概率为\(\frac{n - i}{n}\),所以这项贡献:\(f[i+1] \times \frac{n-i}{n}\)

​ 买当前邮票,贡献为:\(1\)

所以对于一个\(f[i]\),我们可以得出:\(f[i]=f[i] \times \frac{i}{n} + f[i+1] \times \frac{n-i}{n}+1\)

化简得出:\(f[i] = f[i + 1] + \frac{n}{n-i} \dots \ ①\)

那么对于\(g[i]\),推导的方式大致与\(f[i]\)类似:

\(g[i] = (f[i]+g[i]+1) \times \frac{i}{n}+(f[i+1]+g[i+1]+1)\times\frac{n-i}{n}\)

化简得出:\(g[i]=\frac{i}{n-i}\times f[i]+f[i+1]+g[i+1]+\frac{n}{n-i} \dots \ ②\)

至此,两个必要的递推式都推导完毕,接下来就是注意好double的处理,输入和输出,计算等等即可。

AC代码

#include <bits/stdc++.h>

using namespace std;

const int N = 10010;

int n;

double f[N], g[N];

double _div(int a, int b)
{
    return a * 1.00 / b * 1.00;
}

int main()
{
    scanf("%d", &n);
    f[n] = g[n] = 0;
    for (int i = n - 1; i >= 0; i -- )
    {
        f[i] = f[i + 1] + _div(n, n - i);
        g[i] = _div(i, n - i) * f[i] + f[i + 1] + g[i + 1] + _div(n, n - i);
    }
    printf("%.2lf", g[0]);
}
12-30 16:43