洛谷 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]);
}