好久没有写过了,再回来填一填坑。
以前一直以为期望与概率是数学版块里的,结果当它每次和dp一起出现之后,才发现它是数学和dp的结合。而且对于不理解期望概率是啥的萌新表示,真的是连暴力都不会打。而且令人窒息的是这类题的样例也不是很好算,如果不理解题意,真的连样例都都手算不出来。当然这样的好处是一般样例过了就基本上A了。
所以本着学会打暴力的思想,我们学一学概率dp。
先讲一下什么是期望&概率。
首先概率数学上都学过,简单(度娘)来说就是:对于事件A,它的概率定义为: P(A)=mn P ( A ) = m n
,其中
n
n
表示该试验中所有可能出现的基本结果的总数目。表示事件A包含的试验基本结果数。
值得一提的是,在oi当中因为大多数题目中
n
n
和是有限的,所以我们可以通过枚举
n
n
和的个数来计算答案。(但是这样的复杂度通常是
2n
2
n
的)
期望相较于概率在数学出现的不那么频繁,但是在oi中期望真的是概率的好几倍。度娘:在概率论和统计学中,数学期望(mean)(或均值,亦简称期望)是试验中每次可能结果的概率乘以其结果的总和,是最基本的数学特征之一。它反映随机变量平均取值的大小。
用公式表达就是对于事件
i
i
定义它的权值为,那么结果的期望则是:
sumni=1W(i)∗P(i)
s
u
m
i
=
1
n
W
(
i
)
∗
P
(
i
)
。
介绍完概念,我们先来看一道题:CF148D Bag of mice
由于codeforces是英文的,就直接贴洛谷上有翻译的。
在概率题当中,推式子是很重要的,一般式子推出来就做了一大半了。(如图)
fi,j,A
f
i
,
j
,
A
到达表示还剩
i
i
个白色,个黑色且
A
A
为先手的状态的概率。(同理)
其实仔细观察就可以发现f数组的连边其实是一条DAG,所以我们想到了递推。然而这个题还有一个问题就是在DAG上求拓扑序。由于我比较懒,就使用了记忆化搜索。但是在记忆化搜索中,
f
f
不再表示起点到达它的概率,而变成了从它开始A获胜的概率。实际上这道题就又变成了倒推。还有一个坑点是int转double(可能就我会掉坑里)
贴一下代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define A 0
#define B 1
using namespace std;
double f[1005][1005][2];
bool vis[1005][1005][2];
int n,m;
double dp(int w,int b,int p)
{
if(vis[w][b][p]==1) return f[w][b][p];
vis[w][b][p]=1;
if(w==0) return f[w][b][p]=0;
if(p==A)
{
f[w][b][p]+=(double)w/(w+b);
if(b-1>=0)
f[w][b][p]+=(double)b/(w+b)*dp(w,b-1,B);
return f[w][b][p];
}
if(p==B)
{
if(b-1>=0)
f[w][b][p]+=(double)w/(w+b-1)*dp(w-1,b-1,A);
if(b-2>=0)
f[w][b][p]+=(((double)b-1)/(w+b-1))*dp(w,b-2,A);
return f[w][b][p]=(double)b/(w+b)*f[w][b][p];
}
}
int main()
{
//cout<<(double)((2-1)/(1+2-1))*1.0;
scanf("%d%d",&n,&m);
printf("%.9f",dp(n,m,A));
}
做完概率板子,我们来看一看期望:对于一个dag,求它从起点走到终点的期望步数。额,只记得题,不知道在哪儿了。
不过这都没有关系,我们来口头AC一下。
貌似就过了,不过。。。
这tm是对的?我最远才走3步好吧。
刚学期望概率的时候,总有人说,递推要倒退,或者有人说期望倒退,概率正推。这是为什么呢。
观察上图我们发现,我们想用每一个点的值来表示起点到它的概率乘以权值,简单说就是期望步数。但是我们却无法转移这个值,因为我们根本不知道下一步的1应该乘以什么样的权值加入下一个点的期望。而且这样做法有一个致命的问题就是一条路径无法到达终点时,这个方法就没有了意义。
但并不是说不能倒退,既然我们不知道概率,那我们为什么不在算期望的同时计算概率呢?这样的确可以正推。
所以我们不选择正推的方式是因为有点麻烦。
于是就有了倒推,同上,表示
u
u
<script type="math/tex" id="MathJax-Element-35">u</script>到达终点的期望步数。
这样为什么是对的,我们考虑一下,一个点所到达的所有儿子可以向它转移,而且我们知道这个转移的概率,就是加权求和。
最后一点,讲到倒推,记忆化搜索确实是一个神器,虽然正向连边,但是记忆化搜索实际是在回溯是计算值,正好是倒推。而且在一些正推难以找到递推规律的,记忆化搜索也可以解决。这样我们并不需要什么额外操作就可以实现算法,何乐而不为呢?