这题是经典的期望dp题
要想做这道题,我们要先知道期望怎么算
若随机变量X的取值有,一个随机事件可表示为
,对应的概率为
。则称
为随机变量X的数学期望,也就是说数学期望是随机变量取值与概率的乘积之和。
满足线性函数 E(ax+by)=aE(x)+bE(y)
我们再来说一下期望dp的模型
对于任意状态A,已知:
- 状态A所有的后继状态
- 设从状态A 转移到后继状态 B的概率 为 P(A,B),且
- 设从状态A转移到后继状态的B的花费为W(A,B)
求解:从起点状态S到终点状态T的期望花费
一般方法: 将状态视为图上的节点。
设E(A) 表示从状态A到终止状态T的期望花费。设E(T)=0
那么得到状态转移公式:
当转移状态关系不成环时,状态转移为线性的。可以按照拓扑排序递推求解。
绿豆蛙的归宿
再看这道题,我们不妨设dp[i]为从i出发到达终点n的路径期望值。
G[i]为i所有的出边的集合
则:
同时又有一个问题,那就是转移时的过程怎么实现
不妨这样:既然是个DAG(有向无环图),那么我们可以倒着想
我们反向连边,进行一遍拓扑排序,在拓扑排序的时候进行期望dp的转移
Code
#include <iostream>
#include <queue>
#include <vector>
#include <iomanip>
using namespace std;
const int N=1e5+5;
struct Node
{
int v,w;
};
int in[N],out[N];
double dp[N];
int n,m,u,v,w,cnt=0;
queue<int> q;
vector<Node> G[N];
void bfs()
{
q.push(n);//由于“倒过来想”,先加入n
//拓扑排序
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].v,w=G[u][i].w;
dp[v]+=(dp[u]+G[u][i].w)/out[v];//进行状态转移
in[v]--;
if(!in[v]) q.push(v);
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
G[v].push_back(Node{u,w});//建反图
//统计出入度,也要反过来
in[u]++;
out[u]++;
}
bfs();//拓扑排序
cout<<fixed<<setprecision(2)<<dp[1];
return 0;
}