【训练题13:DAG的期望DP】绿豆蛙的归宿 | 洛谷 P4316

绿豆蛙的归宿 | 洛谷 P4316

难度

提 高 + / 省 选 − \color{blue} 提高+/省选- +/

题意

给定一个 DAG N N N 个节点, M M M 条单向边。
每条边有一个移动概率和长度(边权)。
问你从第一个点出发,到最后终点之后,移动的 距离的期望 是多少?

给定了一些特殊的降低题目难度的要求,可以略。

思路

w a , b w_{a,b} wa,b 表示两个节点之间的边权。

我们设 f x f_x fx 表示起点到节点 x x x 的概率。
我们设 f x → v f_{x\rightarrow v} fxv 表示节点 x x x 到节点 v v v 的移动概率。
我们设 e x e_x ex 表示起点到节点 x x x 的距离期望。
我们设 e x ′ e^\prime_x ex 表示从节点 x x x 到终点 n n n 的距离期望。

  • 【逆向推】
    对于从节点 v v v v v v后继节点集合 x ∈ S x\in S xS ,我们有:
    e v ′ = ∑ x ∈ S ( e x ′ + w v , x ) f v → x e^\prime_v=\underset{x\in S}{\sum}(e^\prime_x+w_{v,x})f_{v\rightarrow x} ev=xS(ex+wv,x)fvx
    注意:逆向推需要图建反边,并且需保证起点能经过其他所有中间节点。

  • 【正向推】
    对于从节点 x x x 到节点 x x x后继节点集合 v ∈ S v\in S vS ,我们有:
    f v = ∑ x ∈ S f x × f x → v f_v=\underset{x\in S}{\sum} f_x\times f_{x\rightarrow v} fv=xSfx×fxv
    e v = ∑ x ∈ S ( e x + f x × w x , v ) f x → v e_v=\underset{x\in S}{\sum}(e_x+f_x\times w_{x,v})f_{x\rightarrow v} ev=xS(ex+fx×wx,v)fxv
    最推荐的写法,这样可以求出起点到所有中间节点的期望了
    注意一下边界条件 f 1 = 1 f_1=1 f1=1

  • 【正向推2】
    对于从节点 x x x 到节点 x x x后继节点集合 v ∈ S v\in S vS ,我们有:
    f v = ∑ x ∈ S f x × f x → v f_v=\underset{x\in S}{\sum} f_x\times f_{x\rightarrow v} fv=xSfx×fxv
    e 终 点 = ∑ e d g e   w ( f w a × f w a → w b × w a , b ) e_{终点}=\underset{edge\ w}{\sum}(f_{w_a}\times f_{w_a\rightarrow w_b}\times w_{a,b}) e=edge w(fwa×fwawb×wa,b)
    很好理解,就是算出每条边对终点答案的贡献。
    注意:需要保证每个中间节点都能走到终点。
    注意一下边界条件 f 1 = 1 f_1=1 f1=1

  • 然后需要注意的是,我们递推需要使用拓扑序计算即可。

核心代码

正向推写法:
时间复杂度: O ( N + M ) O(N+M) O(N+M)

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 1e5+50;

vector<pair<int,int> >V[MAX];
int in[MAX];
double f[MAX];
double e[MAX];
queue<int>Q;
int main()
{
    
    
    int n,m;
    scanf("%d%d",&n,&m);
    while(m--){
    
    
        int ta,tb,tc;
        scanf("%d%d%d",&ta,&tb,&tc);
        V[ta].push_back(make_pair(tb,tc));
        in[tb]++;
    }
    for(int i = 1;i <= n;++i)if(!in[i])Q.push(i);
    f[1] = 1;
    while(!Q.empty()){
    
    
        int x = Q.front();
        Q.pop();
        int sz = V[x].size();
        for(auto it : V[x]){
    
    
            int v = it.first;
            int w = it.second;
            double pro = 1.0 / sz;
            f[v] += f[x] * pro;
            e[v] += pro * w * f[x];
            e[v] += pro * e[x];
            in[v]--;
            if(!in[v])Q.push(v);
        }
    }
    printf("%.2lf",e[n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45775438/article/details/109747957