收集邮票 (概率dp)
题目描述
有 (n) 种不同的邮票,皮皮想收集所有种类的邮票。唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是 (n) 种邮票中的哪一种是等概率的,概率均为 (frac{1}{n}) 。但是由于凡凡也很喜欢邮票,所以皮皮购买第 (k) 张邮票需要支付 (k) 元钱。 现在皮皮手中没有邮票,皮皮想知道自己得到所有种类的邮票需要花费的钱数目的期望。
输入格式
一行,一个数字 (N,Nleqslant 10000)
输出格式
要付出多少钱. 保留二位小数
样例
样例输入
3
样例输出
21.25
数据范围与提示
(Nleqslant 10000)
分析
按照概率 (dp) 的套路,我们反向定义方程,反着推,定义 (f[i]) 为已经有了 (i) 种,还需要买几次。 (g[i]) 为已经有了 (i) 种,还需要多少钱。
因为当前已经有了 (i) 种了,每种选的可能性相同,所以这一次选重复的概率为 (frac{i}{n}) ,此时的次数就是 (f[i] + 1) ,因为当前拿了一个重复的,所以还要多拿一次,所以加一。
不重复的概率就是 (frac{n-i}{n}),次数就是 (f[i+1] + 1),因为没拿重复的,所以是拿了 (i+1) 种的步数加一。那么 (f[i]) 的转移就是:
[f[i] = (f[i] + 1) imes frac{i}{n} + (f[i+1] + 1) imes frac{n-i}{n}
]
化简一下就是:
[f[i] = f[i+1] imes frac{n}{n-i}
]
接下来考虑钱数的转移,每一次增加的价格就是取的次数,而拿重复的概率是 (frac{i}{n}),所以这部分就是 ((g[i]+f[i]+1) imes frac{i}{n})。
其次就是没有重复,那么这部分就是 ((g[i+1]+f[i+1]+1) imes frac{n-i}{n})
所以总的就是 :
[g[i] = (g[i]+f[i]+1) imes frac{i}{n} + (g[i+1]+f[i+1]+1) imes frac{n-i}{n}
]
化简完就是:
[g[i] = frac{i}{n-i} imes f[i] + g[i+1] + f[i+1] + frac{n}{n-i}
]
然后倒着枚举,转移就很简单了。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int L = 1 << 20;
char buffer[L],*S,*T;
#define getchar() (S == T &&(T = (S = buffer) + fread(buffer,1,L,stdin),S == T) ? EOF : *S++)
const int maxn = 1e5 + 10;
double f[maxn],g[maxn];
inline int read(){
int s = 0,f = 1;
char ch = getchar();
while(!isdigit(ch)){
if(ch == '-')f = -1;
ch = getchar();
}
while(isdigit(ch)){
s = s * 10 + ch - '0';
ch = getchar();
}
return s * f;
}
int main(){
freopen("D.in","r",stdin);
freopen("D.out","w",stdout);
int n = read();
for(int i = n - 1; ~i ; --i){
f[i] = f[i+1] + (1.0 * n) / (1.0 * (n - i));
g[i] = (1.0 * i) / (1.0 * (n - i)) * (f[i] + 1) + g[i+1] + f[i+1] + 1;
}
printf("%.2lf",g[0]);
return 0;
}