P4450 收集邮票
链接:
题意:
有 \(n\) 种不同的邮票,每次随机购买其中一种,即购买每种邮票的概率是 \(\dfrac 1n\),第 \(i\) 次购买的价格是 \(i\) 元,求买得所有种类邮票的期望价格。
分析:
想到一点,平方的期望不是期望的平方,这也能适用在这道题上,也就是说这道题不能直接用期望次数来算。
然后做法很很神仙,是期望概率dp,做过百事世界杯之旅可能会发现两道题很像,我们可以容易地算出期望次数。设 \(f[i]\) 表示已经买到了 \(i\) 种后还需要买的期望次数,接下来有 \(\dfrac in\) 的概率买到已经有的,有 \(\dfrac{n-i}n\) 的概率买到新的,那么 \(f[i]=\dfrac in(f[i]+1)+\dfrac{n-i}n(f[i+1]+1)\),整理可得 \(f[i]=f[i+1]+\dfrac n{n-i}\),显然 \(f[n]=0\)。
考虑设 \(g[i]\) 表示已经买到了 \(i\) 种后还需要的期望价格,接下来有 \(\dfrac in\) 的概率买到已经有的,有 \(\dfrac{n-i}n\) 的概率买到新的,这里用到一个设计dp时费用提前的trick,也就是说,现在买一次会给以后每次购买加 1 的价格,我们把这个价格提前到这次购买的转移中,状态转移方程就是:
\[g[i]=\frac in(g[i]+f[i]+1)+\dfrac{n-i}n(g[i+1]+f[i+1]+1)
\]
其中的 \(f\) 就是费用提前了,然后后面的 \(+1\) 是这一次的贡献。
整理过后就是
\[g[i]=g[i+1]+f[i+1]+\frac i{n-i}f[i]+\frac n{n-i}
\]
于是只需要同时维护 \(f\) 和 \(g\) 就好了。
算法:
同时维护 \(f\) 和 \(g\),按期望dp方程转移即可 \(O(n)\) 解决。
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
double f[10005],g[10005];
signed main(){
cin>>n;
f[n]=g[n]=0;
for(int i=n-1;i>=0;i--)
f[i]=f[i+1]+double(n)/(n-i),
g[i]=g[i+1]+f[i+1]+double(i)/(n-i)*f[i]+double(n)/(n-i);
cout<<fixed<<setprecision(2)<<g[0];
return 0;
}
题外话:
神仙期望dp题/se