- 数学期望 P=Σ每一种状态*对应的概率。
- 因为不可能枚举完所有的状态,有时也不可能枚举完,比如抛硬币,有可能一直是正面,etc。在没有接触数学期望时看到数学期望的题可能会觉得很阔怕(因为我高中就是这么认为的,对不起何老板了QwQ),避之不及。 但是现在发现大多数题就是手动找公式或者DP推出即可,只要处理好边界,然后写好方程,代码超级简短。与常规的求解不同,数学期望经常逆向推出。
- 比如常规的dp[x]可能表示到了x这一状态有多少,最后答案是dp[n]。而数学期望的dp[x]一般表示到了x这一状态还差多少,最后答案是dp[0]。
具体的看下面的题型吧,看完应该就有感觉了。
- 最后面几道是DP,感觉和数学期望关系不大,不看也罢。
一:Uva12230Crossing Rivers (数学期望)
题目大意:
有个人每天要去公司上班,每次会经过N条河,家和公司的距离为D,默认在陆地的速度为1,
给出N条河的信息,包括起始坐标p,宽度L,以及船的速度v。船会往返在河的两岸,人到达河岸时,
船的位置是随机的(往返中)。问说人达到公司所需要的期望时间。
思路:
1,过每条河最坏的情况是t=*L/v; 即去的时候船刚刚走。
2,过没条河最优的情况是t=L/v; 即去的时候船刚刚来。
3,由于船是均匀发布的,符合线性性质,所以平均下来,过每条河的时间t=*L/v。
(是不是感觉看完后豁然开朗。。。。。。发自内心地说道:原来是尼玛这样一个题,当然这个题并不典型)
二:SPOJ Favorite Dice(数学期望)
题意: 甩一个n面的骰子,问每一面都被甩到的次数期望是多少。
思路: 比较简单常见,公式:初始化dp[]=0; dp[i]=i/n*dp[i]+(n-i)/n*dp[i+1]+1; 化简逆推即可。 求的是dp[0];
三:SGU495Kids and Prizes(数学期望||概率DP||公式)
题意: 有n个奖品,m个人排队来选礼物,对于每个人,他打开的盒子,可能有礼物,也有可能已经被之前的人取走了,然后把盒子放回原处。为最后m个人取走礼物的期望。
思路:
排队取,第1个人取到1个,dp[1]=1;后面的人dp[i]=p取到礼物盒子+dp前面的取到礼物盒子=(n-dp[i-1])/n + dp[i-1]; 当然,也可以化简为公式 printf("%.10lf\n",n*1.0*(1-pow((n-1)*1.0/n,m)));
四:ZOJ3640Help Me Escape(师傅逃亡系列•一) (我自己的逃亡三题)
题意: 师傅被妖怪抓走了。有n个妖怪,每个妖怪有一个固定的战斗力c[],师傅也有一个初始战斗力f0。
每天,师傅会随机选择一个妖怪决斗,如果打得赢ft>c[],就可以逃出去,逃出去要t[]天,毕竟超人不会飞;
否则,师傅会不甘心,当天他会拿出秘籍练功,将自己变强,f(t+)=f(t)+c[],第二天寻找下一次机会。
问师傅能够逃脱可怕的妖怪,继续追求去印度吃手抓饼的梦想的天数的数学期望day。
思路: 设dp[F]是战斗力为F时,逃离的天数期望。(答案是dp[f])。则有公式。 dp[F]= Σ /n * t[i] ,F>c[[i] +∑ /n * dp[F+c[i]] ,F<=c[i]
(第一题是水的,这一题像样一点,列方程。)
五:HDU4035 Maze(师傅逃亡系列•二)(循环型 经典的数学期望)
题意: 师傅又被抓了,师傅现在在一个树里。第一天他在1号节点;对于每一个节点,
有三种可能,一是被妖怪杀死ki,二是被徒儿救走ei,三是第二天等概率地走到相邻的一个节点。
问师傅被救走的天数的期望,不能被救走输出“impossible”。
思路: 上一个题,由于是单调的,没有后续性,所以可以记忆化搜索或者DP解决。
这个题存在后续性,举个例子。如果求从s号节点逃出去的期望dp[s],那么dp[s]和s的子节点和s的父节点有关,而欲求s的子节点时,子节点又和父节点s有关。。。 这个时候就需要我们找一个办法来排除后续性。大概就是找一个很牛逼的公式。这个公式本来是和后续性有关,但是公式之间抵消的后续性。
设 E[i]表示在结点i处,要走出迷宫所要走的边数的期望。E[]即为所求。 叶子结点:
E[i] = ki*E[] + ei* + (-ki-ei)*(E[father[i]] + );
= ki*E[] + (-ki-ei)*E[father[i]] + (-ki-ei); 非叶子结点:(m为与结点相连的边数)
E[i] = ki*E[] + ei* + (-ki-ei)/m*( E[father[i]]+ + ∑( E[child[i]]+ ) );
= ki*E[] + (-ki-ei)/m*E[father[i]] + (-ki-ei)/m*∑(E[child[i]]) + (-ki-ei);
设对每个结点:E[i] = Ai*E[] + Bi*E[father[i]] + Ci;
对于非叶子结点i,设j为i的孩子结点,则
∑(E[child[i]]) = ∑E[j]
= ∑(Aj*E[] + Bj*E[father[j]] + Cj)
= ∑(Aj*E[] + Bj*E[i] + Cj)
带入上面的式子得
( - (-ki-ei)/m*∑Bj)*E[i] = (ki+(-ki-ei)/m*∑Aj)*E[] + (-ki-ei)/m*E[father[i]] + (-ki-ei) + (-ki-ei)/m*∑Cj;
由此可得
Ai = (ki+(-ki-ei)/m*∑Aj) / ( - (-ki-ei)/m*∑Bj);
Bi = (-ki-ei)/m / ( - (-ki-ei)/m*∑Bj);
Ci = ( (-ki-ei)+(-ki-ei)/m*∑Cj ) / ( - (-ki-ei)/m*∑Bj); 对于叶子结点
Ai = ki;
Bi = - ki - ei;
Ci = - ki - ei; 从叶子结点开始,直到算出 A1,B1,C1; E[] = A1*E[] + B1* + C1;
所以
E[] = C1 / ( - A1);
若 A1趋近于1则无解...
六:HDU3853LOOPS (师傅逃亡系列•三)(基础概率DP)
题意: 你知道,师傅经常被抓,这次又被抓到一个矩阵里面,最开始他在Map[][],出口在Map[n][m];
每一次他会消耗两颗神丹,然后每一个格子,有一定概率留在原地,有一定概率向下走一格,有一定概率向右走一格。。。求师傅逃出去的神丹消耗期望。
思路: 这次的逃亡很好想,没有前两次那样需要逆推或者求公式。聪明的你不如手动算一下。实在不行还可以参考下面的题目。
七:ZOJ3551Bloodsucker (数学期望)
题意: 开始有一个吸血鬼,n-1个平民百姓。每天一个百姓被感染的概率可求,问每个人都变成吸血鬼的天数期望
思路: 一般期望题逆推,设dp[i]是目前已经有i个吸血鬼,所有人变成吸血鬼的期望。则dp[n]=;答案是dp[]。(注意这里dp代表的什么) 每一个dp[i]的感染概率可求是p[]=2.0*(n-i)*i/(n-)/n*p; 则可得递推公式: dp[i] = (dp[i+]*p[]+)/p[];
八:ZOJ3329One Person Game(循环型 数学期望)
题意: 有三个骰子,面值分别是k1,k2,k3。每次扔出的值之和加到ans上,问多少次才能ans>n;当然,当遇到k1=a,k2=b,k3=c时,ans=;重新开始累加。
思路: 和之前第五题Maze一个题型。写出的公式是有后续性的。我们需要弄一个递推公式,消去后续性。(当然循环的话高斯消元也可以做。)
设dp[i]表示达到i分时到达目标状态的期望,pk为投掷k分的概率,p0为回到0的概率
则dp[i]=∑(pk*dp[i+k])+dp[]*p0+;
都和dp[]有关系,而且dp[]就是我们所求,为常数
设dp[i]=A[i]*dp[]+B[i];
代入上述方程右边得到:
dp[i]=∑(pk*A[i+k]*dp[]+pk*B[i+k])+dp[]*p0+
=(∑(pk*A[i+k])+p0)dp[]+∑(pk*B[i+k])+;
明显A[i]=(∑(pk*A[i+k])+p0)
B[i]=∑(pk*B[i+k])+
先递推求得A[]和B[].
那么 dp[]=B[]/(-A[]);
大概就是这个样子。
九:CF 148D D. Bag of mice (概率DP||数学期望)
题意: 一对情侣开房玩抓老鼠游戏,老鼠有黑白两色,女的为先手,先抓到白老鼠胜。
特别的,男的每抓一只老鼠后,还会随机放走一只老鼠。问女的赢的概率是多少。如果输了,后果会很严重,当天晚上只能睡沙发。
思路: dp[i][j]为当前状态,有i只白老鼠,j只黑老鼠,女的赢的概率。那么dp[][] = 这一次赢 + 以后赢= i/(i+j) + 。。。具体见代码。
十:POJ3682King Arthur's Birthday Celebration(数学期望||概率DP)
题意: 有一个富豪,他决定每天撒钱,并且抛硬币,第一天1块钱,第二天3块钱,第三天5块,直到他抛到硬币向上的数量为K。 求天数期望和钱期望。
思路: 天数期望dp很好求,公式一推,代码一敲。钱期望money没想出来,我开始想难道是用第x天结束的期望乘第x天的钱,累加,直到x天的期望乘钱小于0.。 但是参考了下别人的公式,反正自己是没想出来。 天数:dp[i]=dp[i]*(-p)+dp[i-]*p+,化简:dp[i]=dp[i-]+/p; money:money[i] = p(money[i-]+ *(dp[i-]+)-) + (-p)(money[i] + * (dp[i]+)-)。 化简:money[i]=money[i-]+*dp[i-]-*dp[i]+(+*dp[i])/p; 问题:
可以用巴斯卡分布?二项分布???给数学跪了 http://blog.csdn.net/nmfloat/article/details/50650489
十一: POJ2151Check the difficulty of problems (组合数学||概率DP)
题意: 一套题,有T个题,M个人应考,已知每个人做来某题的概率。问X的概率。X满足,每个考生至少做来一道题。至少有一人做的题不少于N道。
思路: 不算是很典型的概率DP,更像是一道简单数学题。 可以把所有考生都至少做来一道题的概率减去 每个人都做来1到n-1道题的概率。 p=[(-x11)*(-x12)(..) ] * [(-x21)*(-x22)(..)]*[...] - [...]*[...] ,这样的话,用组合数就ok了。 但是这里是用的DP是思路,先把考生与考题的关系求出来,p[i][j][k] 表示第i个考试前j个题会做k道的概率。再根据题意进行DP。
十二:HihoCoder1164 随机斐波那契(概率DP)
描 大家对斐波那契数列想必都很熟悉: a0 = , a1 = , ai = ai- + ai-,(i > )。 现在考虑如下生成的斐波那契数列: a0 = , ai = aj + ak, i > , j, k从[, i-]的整数中随机选出(j和k独立)。 现在给定n,要求求出E(an),即各种可能的a数列中an的期望值。(1<=n<=500)
思路:
不说了,数据小,我暴力枚举的。
十三:HihoCoder 1075 开锁魔法III(概率DP+组合)
描述 一日,崔克茜来到小马镇表演魔法。 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它。
初始时,崔克茜将会随机地选择 k 个盒子用魔法将它们打开。崔克茜想知道最后所有盒子都被打开的概率。
,每个盒子都有一个入度和一个出度,以之前二分图拆点的经验来看,必然会形成很多个环。 ,每个环至少选择一个盒子。 ,每个环至少选择一个盒子的组合数,联想到母函数,组合数。 .*YY。可以DP,但是误差可能大一些。可以全部求出来再除,这样误差小一些。 (ps:学会了母函数再搞组合是要多一分灵感!弯的four)
十四:BZOJ - 4318: OSU! (期望DP&Attention)
题意:一段连续长度为L的线段贡献是L^,求贡献的期望。 (注意期望的平方和求方的期望不一样
思路:此类期望题都是单独算某一位的贡献,假设前一位的连续长度为g[i-],那么很明显当前位的期望长度为 g[i]=(g[i-]+)*p[i]; 则当前为的贡献是add=g[i]^-g[i-]^=*g[i]^-*g[i]+。 这三部分分别算期望即可。 第一部分:*g[i]^,就是平方的期望(不仅仅是期望的平方那么简单),令期望的平方为数组g2,则3g2[i]=*(g2[i-]+*g[i-]+)*p[i]; 第二部分:-*g[i],其期望=-*(g[i-]+)*p[i] 第三部分: ,其期望=p[i]
主要就是要注意期望的平方如何去算!
-------------------------------------------------------------分界线-----------------------------------------------------------------------------
2019-05-12更新
由于上诉都是一些比较简单的东西,而好像又容易被搜索到,所以加点干货。
干货一: 反复模拟,逼近答案。
(有的题目,由于大方向确定,我们只需要在小范围内一层一层不断逼近即可;或是随机多次也能得到比较正确的结果。
CodeForces - 24D :Broken robot (高斯消元 随机)
pro:给定N*M的矩阵,以及初始玩家位置。 规定玩家每次会等概率的向左走,向右走,向下走,问走到最后一行的期望。保留4位小数。
sol:可以列出方程,高斯消元即可,发现是三角矩阵,O(N^2)。 也可以用反复逼近答案。 反复做,dp[i][j]=(dp[i][j+1]+dp[i][j-1]+dp[i][j]+dp[i-1][j])/d[j]+1.0 为了使逼近效果更好,我每次先左一次,再右一次。 虽然 暴力但是效率也不差。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=;
double dp[maxn][maxn]; int d[maxn];
int main()
{
int N,M,x,y;
scanf("%d%d%d%d",&N,&M,&x,&y);
rep(i,,M){
d[i]=;
if(i>) d[i]++;
if(i<M) d[i]++;
}
rep(i,x+,N){
rep(t,,){
rep(j,,M) dp[i][j]=(dp[i][j+]+dp[i][j-]+dp[i][j]+dp[i-][j])/d[j]+1.0;
for(int j=M;j>=;j--) dp[i][j]=(dp[i][j+]+dp[i][j-]+dp[i][j]+dp[i-][j])/d[j]+1.0;
}
}
printf("%.10lf\n",dp[N][y]);
return ;
}
干货二: Min-Max容斥。
(有的题目,数据量比较小,但是方程很难列出,我们可能要考虑容斥,min-max容斥是一种比较常见的容斥方式。 他解决问题的方式:假设有S个对象,求把所有东西都取到的期望,不直接求,而是通过求子集的期望,然后容斥得到结果。
HDU - 4336:Card Collector(min-max容斥求期望)
pro:题面不好看,题意很简单,就是给定S个物品,然后每次取到物品i的概率为pi,∑pi<=1; 求把所有物品都至少取到一次的期望。
sol: T是S的子集,我们得到每个子集T的期望,然后乘上容斥系数,累加起来就是答案。 假设我们dfs得到了S的子集T,并且得到至少取到这个子集的一个的概率p,则其期望为1/p;
#include<bits/stdc++.h>
using namespace std;
const int maxn=;
double p[maxn],ans; int N;
void dfs(int pos,double now,int opt)
{
if(pos==N+) {
if(opt>){
if(opt&) ans+=1.0/now;
else ans-=1.0/now;
}
return ;
}
dfs(pos+,now,opt);
dfs(pos+,now+p[pos],opt+);
}
int main()
{
while(~scanf("%d",&N)){
for(int i=;i<=N;i++) scanf("%lf",&p[i]);
ans=; dfs(,0.0,);
printf("%.4lf\n",ans);
}
return ;
}