数学期望DP学习笔记

数学期望: 在概率论和统计学中,数学期望(mathematic expectation)(或均值,亦简称期望)是试验中每次可能结果的概率乘以其结果的总和,是最基本的数学特征之一。它反映随机变量平均取值的大小。——摘自百度百科
不懂?太正常了,百度百科就是不写人话。
举个栗子解释一下:在一次膜你赛中,小 z 预估自己有 \(0.5\) 的概率考 \(300\) 分,\(0.3\) 的概率考250分,\(0.2\) 的概率考 \(200\)分,计算方法与加权平均值有些类似,那她得分的数学期望即是: \(0.5\times 300+0.3\times 250+0.2\times 200=265pts\)
具体地,记第 \(i\) 种结果的概率为 \(p_i\),结果得分为 \(f_i\),那么 \(x\) 的期望 \(E(x)=\sum_{i=1}^{x}p_i\times f_i\)
注意,\(\sum_{i=1}^{x}p_i=1\),即所有概率总和必须是 \(1\)


Problem1 P4316 绿豆蛙的归宿
题意简述
给出一张 DAG,保证连通图,随机行走,求 \(1\)\(n\) 的路径长度期望。具体地,若当前在点 \(u\),出边数为 \(od_u\),则走到每一条出边的概率相等,都为 \(\frac{1}{od_u}\)
题目解析
我们设 \(f_i\) 表示从 \(1\)\(i\) 的路径长度期望,答案显然是 \(f_n\)
但手算样例,我们看到如下情况,\(f_1\) 显然为 \(0\)\(f_2\) 易得为 \(0.5\)\(f_3\) 如何计算?
p9LHnqH.png
显然我们还需要考虑从 \(1\)\(3\) 的概率。但我们感觉这种方法略微繁琐,正推不方便,不妨逆推,也就是设 \(f_i\) 表示从 \(i\)\(n\) 的路径长度期望,此时答案为 \(f_1\),初始状态 \(f_n=0\)
可以得出转移方程,当前在点 \(u\)\(f_u=\frac{1}{od_u}\times \sum f_v+w_{u,v}\)
实现中,我们要逆推,所以两种方法处理图:记忆化搜索或者建反图拓扑排序。
代码实现
\(1.\) 记忆化搜索

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e5+10;
int n,m;
int x,y;
double z;
struct edge{int v,next;double w;}e[N<<1];
int head[N],tot;
inline void add(int u,int v,double w){e[++tot].v=v,e[tot].w=w,e[tot].next=head[u],head[u]=tot;}
double chu[N];
double f[N];

void dfs(int u){
	if(f[u]) return ;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].v;
		if(!f[v]) dfs(v);
		f[u]+=f[v]+e[i].w;
	}
	if(chu[u]) f[u]/=chu[u];
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i) scanf("%d%d%lf",&x,&y,&z),add(x,y,z),chu[x]++; 
	dfs(1);
	printf("%.2lf",f[1]);
	return 0;
}

\(2.\) 反图拓扑

#include<queue>
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+100;
const int M=2*N;
int n,m;
struct node{
	int ver,nxt;
	double edge;
}e[M];
int head[N],tot;
int in[N],out[N];
double f[N],g[N];
void add(int x,int y,double z){
	e[++tot]={y,head[x],z};
	head[x]=tot;
}
queue<int> q;
void topsort(){
	f[1]=0.0;
	g[1]=1.0;
	q.push(1);
	while(q.size()){
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=e[i].nxt){
			int y=e[i].ver;
			double z=e[i].edge;
			f[y]+=(f[x]+g[x]*z)/out[x];
			g[y]+=g[x]/out[x];
			in[y]--;
			if(in[y]==0){
				q.push(y);
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y;
		double z;
		scanf("%d%d%lf",&x,&y,&z);
		add(x,y,z);
		in[y]++;
		out[x]++;
	}
	topsort();
	printf("%.2lf\n",f[n]);
	return 0;
}

(法 \(2\) 代码来自 此间的少年fu)。


Problem2 蓝桥杯 2022 省赛 A 组 E 题 (蓝桥杯终于出了个好题。)
题意简述
有一只虫想要爬上高度为 \(n\) 的树,初始位于树根, 即高度为 \(0\),当它从高度 \(i-1\) 爬到高度 \(i\) 时有 \(p_i\) 的概率掉回树根, 求它从树根爬到树顶时,花费总时间的数学期望(一次尝试向上爬花费时间为 \(1\))。
题目解析
类似的套路,我们设 \(f_i\) 表示从 \(i\) 爬到 \(n\) 时间的数学期望,答案即为 \(f_0\)
则从 \(i\) 成功上到 \(i+1\) 的概率是 \(1-p_{i+1}\),掉回树根的概率 \(p_{i+1}\),初始状态 \(f_n=0\)
易得:\(f_i=1+(1-p_{i+1})\times f_{i+1}+p_{i+1}\times f_0\)
发现此时的 \(f_i\)\(f_{i+1}\)\(f_0\) 有关。有后效性的 DP?怎么办呢?
我们尝试多写几项寻找一下规律:
\(f_0=1+(1−p_1)\times f_1+p_1\times f_0\)
\(f_1=1+(1−p_2)\times f_2+p_2\times f_0\)
\(f_2=1+(1−p_3)\times f_3+p_3\times f_0\)
解方程组……高斯消元会 TLE……
于是直接代入可得 \(f_0=1+(1-p_1)+(1-p_1)\times (1-p_2)+(1-p_1)\times (1-p_2)\times (1-p_3)\times f_n+[p_1+(1-p_1)\times p_2+(1-p_1)\times (1-p_2)\times p_3]\times f_0\)

posted @ 2023-05-27 12:10  Mr_KaYa  阅读(81)  评论(0)    收藏  举报