记忆化搜索(dfs+memo)无环有向图

发布于:2025-07-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

这是一道可以当作板子的极简记忆化搜索 

 

建立a 是邻接表,其中 a[x] 存储从节点 x 出发能到达的所有节点。

b[x] 记录从节点 x 出发的所有边的权重之和。根据数学原理,我们很容易发现,一个根(起点)的期望,等于他所有子的期望加上边权和,除边

 

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, m, x, y, z;
vector<vector<int>> a(N); //邻接表
double b[N]; //记录从节点 x 出发的所有边的权重之和。

double dfs(int x) {
    if (x == n) return 0.0; //到达终点,期望为0

    double o = 0.0;
    int p = a[x].size();
    for (auto i : a[x]) {//所有子的期望和
        o += dfs(i);
    }
    o += b[x];//加上边权和
    return o / p;//期望
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        cin >> x >> y >> z;
        a[x].push_back(y);
        b[x] += z;
    }
    double o = dfs(1);
    printf("%.2lf\n", o); 
    return 0;
}

这样写出来,思路是对的,但是在多条路径下,我们明显可以发现,每个子节点都会计算很多次,所以我们使用记忆化来优化;

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, m, x, y, z;
vector<vector<int>> a(N); 
double b[N]; 
double memo[N];  //储存
bool vis[N]; //判断是否被计算过

double dfs(int x) {
    if (vis[x]) return memo[x];//如果计算了,直接返回结果
    if (x == n) return 0.0; 
    vis[x] = true;
    double o = 0.0;
    int p = a[x].size();
    for (auto i : a[x]) {
        o += dfs(i);
    }
    if (p == 0) return 0.0;  
    o += b[x];
    return memo[x]=o / p;//返回时给memo记录
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        cin >> x >> y >> z;
        a[x].push_back(y);
        b[x] += z;
    }
    memset(vis, false, sizeof(vis));//初始化
    double o = dfs(1);
    printf("%.2lf\n", o); 
    return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到