可以到达每一个节点的最少边反转次数
给你一个 n 个点的 简单有向图 (没有重复边的有向图),节点编号为 0 到 n - 1 。如果这些边是双向边,那么这个图形成一棵 树 。
给你一个整数 n 和一个 二维 整数数组 edges ,其中 edges[i] = [ui, vi] 表示从节点 ui 到节点 vi 有一条 有向边 。
边反转 指的是将一条边的方向反转,也就是说一条从节点 ui 到节点 vi 的边会变为一条从节点 vi 到节点 ui 的边。
对于范围 [0, n - 1] 中的每一个节点 i ,你的任务是分别 独立 计算 最少 需要多少次 边反转 ,从节点 i 出发经过 一系列有向边 ,可以到达所有的节点。
请你返回一个长度为 n 的整数数组 answer ,其中 answer[i]表示从节点 i 出发,可以到达所有节点的 最少边反转 次数。
1. 换根dp
计算某一个节点需要的反转次数比较简单,经过一次dfs即可
要计算所有节点的反转次数,必然不能对每个点进行计算
而是根据上一次的结果,直接运算出当前结果
class Solution {
public:
vector<int> minEdgeReversals(int n, vector<vector<int>> &edges) {
vector<vector<pair<int, int>>> g(n);
for (auto &e: edges) {
int x = e[0], y = e[1];
g[x].emplace_back(y, 1);
g[y].emplace_back(x, -1); // 从 y 到 x 需要反向
}
vector<int> ans(n, 0);
function<void(int, int)> dfs = [&](int x, int fa) { //从0跑一次深度优先,计算0开始的翻转次数
for (auto &[y, dir]: g[x]) { //遍历相邻节点
if (y != fa) { //不返回原路
ans[0] += dir<0; //计算需要翻转的次数
dfs(y, x);
}
}
};
dfs(0, -1);
function<void(int, int)> reroot = [&](int x, int fa) {//换根dp
for (auto &[y, dir]: g[x]) {
if (y != fa) {
ans[y] = ans[x] + dir; // dir 就是从 x 换到 y 的「变化量」
reroot(y, x);
}
}
};
reroot(0, -1);
return ans;
}
};