1018 - 数学期望 - 绿豆蛙的归宿(luogu 4316)

版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 https://blog.csdn.net/weixin_42557561/article/details/83147362

传送门

分析

数学期望入门题

数学期望:随机变量取值与概率的乘积之和

线性性:就是可以各种相加求解,然后乱搞dp

正经的,也就是说对于不相关的两个随机变量φ和ξ,E(φ±ξ)=E(φ)±E(ξ);E(φξ)=E(φ)E(ξ);E(φ/ξ)=E(φ)/E(ξ)

 摘自洛谷___new2zy___ :

根据题目要求,我们很自然的可以想到:

设状态f[x]f[x]表示点x到终点n的期望路径总长

显然,要求的答案为f[1],而且有f[n]=0f[n]=0

(终点到自己的期望距离肯定为0啊。。。)

发现这时就是期望dp的套路了。。。

正好要将期望dp,不妨我们先来说说期望dp的具体sao操作

期望dp,也加概率dp

一般来说,期望dp找到正确的状态后,转移是比较容易想到的。

但一般情况下,状态一定是“可数”的

事实上,将问题直接作为dp的状态是最好的。

如,问“n人做XX事的期望次数”,那么不妨设计状态为f[i]表示i个人做完事的期望

转移一般是递推,通常分两种,一种是从上一个状态转移得(填表法),另一种是转移向下一个状态(刷表法)。

有时期望dp需以最终状态为初始状态转移,即逆推

如f[i]表示期望还要走f[i]步到达终点。这种状态的转移是刷表法

形如f[i]=∑p[i→j]*f[j]+w[i→j]f[i]=∑p[i→j]∗f[j]+w[i→j],其中p表示转移的概率w表示转移对答案的贡献

一般来说,初始状态确定时可用顺推,终止状态确定时可用逆推。

大概期望dp的套路就是这样了吧。。。(我还是菜讲得不太好)

现在我们回到本题

上面提到了,我们设状态f[x]表示点x到终点n的期望路径总长,那么显然有f[n]=0

那么这正好符合了“终止状态确定时可用逆推”的策略套路

具体来说:

对于一条有向边,我们假设它由 x->yx−>y

那么有f[x]=(\dfrac{1}{degree[x]})*∑f[y]+w[x->y]f[x]=(degree[x]1​)∗∑f[y]+w[x−>y]

其中degree[x]degree[x]表示x点的度(结合一下上面给出的式子你就懂了)

仔细观察题目其实你会发现, (\dfrac{1}{degree[x]})(degree[x]1​)其实就是概率(p)

同时又有一个问题,那就是转移时的过程怎么实现

不妨这样想:既然是个DAG,那么我们可以“倒过来”想

具体来讲,我们反向连边,进行一遍拓扑排序,在拓扑排序的时候进行期望dp的转移

这时候要注意上面的x和y要反过来(因为我们反向连边了)

那么我们转移方程就设计完啦

(其实还是挺好理解的是不是)(逃

分析一下复杂度

dp转移是与拓扑排序有关的,每次计算几乎是O(1)O(1)的

那么时间复杂度瓶颈就是拓扑排序,故时间复杂度为O(n+m)O(n+m)

代码

#include<bits/stdc++.h>
#define N 100009
using namespace std;
int n,m;
int nxt[N<<1],head[N],to[N<<1],w[N<<1],cnt=0;
void add(int x,int y,int z){
	nxt[++cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
int num[N],ru[N];
double dis[N];
queue<int > Q;
int main(){
	scanf("%d%d",&n,&m);
	int i,j,k,u,v,z;
	for(i=1;i<=m;++i){
		scanf("%d%d%d",&u,&v,&z);
		add(v,u,z);ru[u]++;num[u]++;
	}
	dis[n]=0;Q.push(n);
	while(!Q.empty()){
		int u=Q.front();Q.pop();
		for(int e=head[u];e;e=nxt[e]){
			int v=to[e];
			dis[v]+=(dis[u]+w[e])/num[v];
			ru[v]--;
			if(!ru[v]) Q.push(v);
		}
	}
	printf("%.2lf",dis[1]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/83147362