收集邮票(数学期望)
这个题是真的神仙。。。
根本不会做啊qwq
蒟蒻只会抄题解啊qwq
抄完题解感觉内心愧疚,只好写一写自己的理解+注释来发一篇博客补偿一下qwq
希望能把神仙思路传递给其他看到这个题不太会做的dalao们。。。。。
--------------------------------------------------------------以上都是废话------------------------------------------------------------------------------
期望DP和概率DP不一样,一般都是倒着推的。(dalao的经验之谈)
至于为什么倒着推,大家可以去参照网上的其他博客,反正我也不是很懂(太蒻了)。如果有哪位大佬会,可以私信我谢谢qwq。
--------------------------------------------------------------以上还是废话-------------------------------------------------------------------------------
好的,进入正题。
题目要求的是得到所有邮票需要花费的钱数的数目期望。
我们设一个\(g[i]\)数组来表示当前收集了i种邮票,到完成收集所有种类的邮票还需要的花费,\(f[i]\)数组表示当前收集了i种邮票,到完成收集所有种类的邮票还需要的次数那么我们考虑怎么从后往前倒着DP。
我们把状态抽离出来,每个状态都表示现在已经有i种邮票,那么我们可以很显然地找出相邻状态之间的关系:对于第i个状态,它有\(i/n\)的概率选中已经选择过的邮票,那么状态不变,除此之外,还有\((n-i)/n\)的概率选中没有选到的邮票,那么就转移到了下一个状态i+1.
因为蒟蒻我太蒻了,原先没有怎么做过期望的题,所以我对倒推还是有些疑惑的,下面我要稍微解释一下递推中每次支付钱数的转移问题,如果大佬们理解就不用看下一段了qwq
现在我们要找状态转移方程,考虑\(g[i]\)怎么从\(g[i+1]\)转移过来。但是在这之前我们要搞清楚一件事情:原题中说“购买第k张邮票需要支付k元钱(也就是第k轮的时候支付k元钱,而不是第k种邮票支付k元钱)”,为了方便理解,我们设一共会期望一共会有p轮,那么顺着推的时候权值肯定是1到p的数列。但是现在我们倒着推了,所以我们现在把它转换成距离达成目标还有k步的时候支付k元钱(也就是正着看的话是p到1的数列),其实这在本质上是一样的。至于为什么能想到要开\(f\)数组,其实就是要记录步数,来获得该回合需要支付的钱数。
\(f\)数组的转移比较简单:
就是前面提到的转移,然后因为原先拿了i+1种邮票,现在只有i种邮票,所以就是又进行了一步,所以肯定是要加上一步的次数(最后+1)
\(g\)数组有点神仙:
前面的转移还是和上面的思路一样,后面的是要加上转移到自己+转移到下一个状态的和,+1的解释还是和上面一样qwq
下面是代码:(注意精度问题,可以开long double比较保险)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 10010
using namespace std;
int n;
long double f[MAXN],g[MAXN];
int main()
{
scanf("%d",&n);
for(int i=n-1;i>=0;i--) f[i]=f[i+1]+(long double)n/(n-i);
for(int i=n-1;i>=0;i--) g[i]=((long double)i/(n-i))*f[i]+g[i+1]+f[i+1]+((long double)n/(n-i));
printf("%.2lf\n",(double)g[0]);
return 0;
}