(我一直以为我三四天前就发出来了)
最近集训的时候学习了概率与组合的内容,恰巧今天又学了概率dp和期望dp,个人觉得超级有意思,比其他dp有趣多了,借机分享一下自己的理解心得。(我才不会告诉你是因为别的dp太难了我学不会呜呜)
概率DP
今天qko学长讲课的时候对概率dp的概括特别到位:“概率dp一般求的是实际的结果。在dp过程中,当前状态是由所有子状态的概率共同转移而来,所以概率dp只是利用了dp的动态而没有规划(即只需转移无需决策)。”
其实至今我也对需要规划的dp迷迷糊糊,对需要决策的dp迷迷糊糊qaq,所以对一个式子走天下的概率dp好感有加。
讲道理我对概率dp的理解更像是高中时候的递推式,你要做的就是把递推式算出来
,从前向后(单纯概率dp)或者从后向前(期望dp)遍历算一遍即可。算法十分简单,代码无敌好写,比较不容易的就是递推式的推导。
例题(单纯概率dp)
传送门
Rimi learned a new thing about integers, which is - any positive integer greater than 1 can be divided by its divisors. So, he is now playing with this property. He selects a number N. And he calls this D.
In each turn he randomly chooses a divisor of D (1 to D). Then he divides D by the number to obtain new D. He repeats this procedure until D becomes 1. What is the expected number of moves required for N to become 1.
简述一下就是,给你一个数,用它的因子去除它,循环重复这个过程,直至这个数变成1,问需要多少次。
看起来问多少次是一个期望问题,但这个不是单纯的期望dp,举个例子,比如50。50的因子为{1,2,5,10,25,50}。显而易见,用因子去除一个数剩下的数还是他的因子,假如现在剩下的数是10,那么问题就转化为了把10变成1需要几次的问题。从这里我们可以看出,50需要的次数(我们记为E(50))取决于1,2,5,10,25,50出现的次数。这就注定了我们的计算顺序是从前往后依次计算的。
假设数X的因子个数是为N,则
f(X) = 1/N * (f(X1) + f(X2) + … +f(X))
由于在此时f(X)在此时是未知的,我们呢就将式子化简一下。
最终化简成 f (X) = 1/(N-1) * (f(X1) + f(X2) + … +f(X(N-1)))
用一个函数在之前将左右的f(x)算好即可。
代码么得什么难度~
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
double f[100009];
void ans()
{
f[1] = 0;
for(int i=2;i<=100000;i++)
{
int cnt = -1;//保证最后加完是n-1
double sum =0