题目-绿豆蛙的归宿

问题分析

图的性质是, 从起点开始能到达图中所有点, 图中所有点也能到达 n n n号点
如果遇到岔路, 会以等概率跳到下一个位置, 要求计算的是路径的期望

因为初始状态是唯一的, 下一个状态不唯一
因此可以定义状态表示 f ( i ) f(i) f(i), 表示从 i i i到终点的路径期望, 初始状态是 f ( n ) = 0 f(n) = 0 f(n)=0, 目标状态是 f ( 1 ) f(1) f(1)


对于当前位置 u u u如何计算 f ( u ) f(u) f(u)?
假设 w i w_i wi是边权, 根据期望的性质
f ( u ) = ∑ i = 1 k 1 k ⋅ ( f ( s i ) + w i ) f(u) = \sum _{i = 1} ^ k \frac{1}{k} \cdot (f(s_i) + w_i) f(u)=i=1kk1(f(si)+wi)

算法步骤

算法只需要实现上述的公式, 从后向前递推, 因为是拓扑图的递推关系, 不存在环

  • 首先计算拓扑序
  • 按照拓扑序计算状态 f ( i ) f(i) f(i)

因为每个点只会被考虑一次, 算法时间复杂度 O ( n ) O(n) O(n)

代码实现

t o p top top排序 + 逆序递推代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10, M = N << 1;

int n, m;
int head[N], ed[M], ne[M], w[M], idx;
int din[N], dout[N];
double f[N];
int q[N], h = 0, t = -1;

void add(int u, int v, int val) {
   
    ed[idx] = v, ne[idx] = head[u], w[idx] = val, head[u] = idx++;
}

void top_sort() {
   
    for (int i = 1; i <= n; ++i) {
   
        if (!din[i]) q[++t] = i;
    }

    while (h <= t) {
   
        int u = q[h++];
        for (int i = head[u]; ~i; i = ne[i]) {
   
            int v = ed[i];
            din[v]--;
            if (!din[v]) q[++t] = v;
        }
    }
}

int main() {
   
    ios::sync_with_stdio(false);
    cin.tie(0);

    memset(head, -1, sizeof head);
    cin >> n >> m;

    for (int i = 0; i < m; ++i) {
   
        int u, v, w;
        cin >> u >> v >> w;
        din[v]++;
        dout[u]++;
        add(u, v, w);
    }

    top_sort();

    for (int i = t; i >= 0; --i) {
   
        int u = q[i];
        for (int j = head[u]; ~j; j = ne[j]) {
   
            int v = ed[j];
            f[u] += (f[v] + w[j]) / dout[u];
        }
    }

    printf("%.2lf\n", f[1]);
    return 0;
}

记忆化搜索实现代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10, M = N << 1;

int n, m;
int head[N], ed[M], ne[M], w[M], idx;
int dout[N];
double f[N];

void add(int u, int v, int val) {
   
    ed[idx] = v, ne[idx] = head[u], w[idx] = val, head[u] = idx++;
}

double dp(int u) {
   
    if (f[u] >= 0) return f[u];
    f[u] = 0;
    for (int i = head[u]; ~i; i = ne[i]) {
   
        int v = ed[i];
        f[u] += (dp(v) + w[i]) / dout[u];
    }
    return f[u];
}

int main() {
   
    ios::sync_with_stdio(false);
    cin.tie(0);

    memset(head, -1, sizeof head);
    cin >> n >> m;

    for (int i = 0; i < m; ++i) {
   
        int u, v, w;
        cin >> u >> v >> w;
        dout[u]++;
        add(u, v, w);
    }

    memset(f, -1, sizeof f);
    dp(1);

    printf("%.2lf\n", f[1]);
    return 0;
}