From 5083079c0e08c4887c5052673a3b9cdb89def98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 9 Jan 2024 15:29:35 +0800 Subject: [PATCH] 'commit' --- TangDou/Topic/HuanGenDp/P1364_dfs.cpp | 47 ++++++++++++--------- TangDou/Topic/【换根】dfs专题.md | 56 +++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/TangDou/Topic/HuanGenDp/P1364_dfs.cpp b/TangDou/Topic/HuanGenDp/P1364_dfs.cpp index 54c64ba..612ce41 100644 --- a/TangDou/Topic/HuanGenDp/P1364_dfs.cpp +++ b/TangDou/Topic/HuanGenDp/P1364_dfs.cpp @@ -1,42 +1,49 @@ #include using namespace std; -const int N = 110; +const int N = 110, M = N << 1; const int INF = 0x3f3f3f3f; int n; -int a[N], st[N]; // b 数组存是否遍历过这个节点 -int e[N][10]; // 存储树 -int dis[N][N]; // 存节点间的距离 -int cnt; +int x[N]; // 点权权值数组 +int st[N]; // st 数组存是否遍历过这个节点 +int dis[N][N]; // 存节点间的距离 -void dfs(int u, int x) { // x 表示起点 - st[u] = 1; - dis[x][u] = cnt, dis[u][x] = cnt; - cnt++; - for (int i = 1; i <= e[u][0]; i++) { // 枚举子节点 - if (st[e[u][i]]) continue; - dfs(e[u][i], x); +// 链式前向星 +int e[M], h[N], idx, w[M], ne[M]; +void add(int a, int b, int c = 0) { + e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; +} + +void dfs(int root, int u, int step) { // root 表示根,u:当前走到哪个节点,step:到u点时走了几步 + st[u] = 1; // u走过了,防止回头路 + dis[root][u] = step, dis[u][root] = step; // root<->u之间的路径长度 + + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; + if (st[v]) continue; + dfs(root, v, step + 1); } - cnt--; // 退回上一个节点,要记得把距离减去一 } int main() { + // 初始化链式前向星 + memset(h, -1, sizeof h); + cin >> n; for (int i = 1; i <= n; i++) { - int x, y; - cin >> a[i] >> x >> y; - if (x) e[i][++e[i][0]] = x, e[x][++e[x][0]] = i; // 存图 - if (y) e[i][++e[i][0]] = y, e[y][++e[y][0]] = i; + int a, b; + cin >> x[i] >> a >> b; + if (a) add(i, a), add(a, i); // 存图 + if (b) add(i, b), add(b, i); } for (int i = 1; i <= n; i++) { memset(st, 0, sizeof st); - cnt = 0; // 初始化 - dfs(i, i); // 搜索 + dfs(i, i, 0); // 搜索 } int ans = INF; for (int i = 1; i <= n; i++) { int s = 0; for (int j = 1; j <= n; j++) - s = s + a[j] * dis[i][j]; // 累加距离 + s = s + x[j] * dis[i][j]; // 累加距离 ans = min(ans, s); } cout << ans << endl; diff --git a/TangDou/Topic/【换根】dfs专题.md b/TangDou/Topic/【换根】dfs专题.md index 793dceb..1df0257 100644 --- a/TangDou/Topic/【换根】dfs专题.md +++ b/TangDou/Topic/【换根】dfs专题.md @@ -284,10 +284,64 @@ $n$ 的值很小,最多可以有 $O(n^3)$ 的时间复杂度。 这样就需要求出节点之间的距离。先枚举起点,然后算出每个节点到这个起点间的距离。我用的是一个朴素的 $dfs$,在搜索的过程中累加距离,每搜索到一个节点,就储存这个节点与起点间的距离。 -而累加距离也很容易实现,在从一个节点遍历到下一个节点时,$cnt$ 增加 $1$;而退回上一个节点时,$cnt$ 减去 $1$。 +而累加距离也很容易实现,在从一个节点遍历到下一个节点时,$step$ 增加 $1$; 代码就很好实现了,时间复杂度也不高,$O(n^2)$。 +```cpp {.line-numbers} +#include +using namespace std; +const int N = 110, M = N << 1; +const int INF = 0x3f3f3f3f; +int n; +int x[N]; // 点权权值数组 +int st[N]; // st 数组存是否遍历过这个节点 +int dis[N][N]; // 存节点间的距离 + +// 链式前向星 +int e[M], h[N], idx, w[M], ne[M]; +void add(int a, int b, int c = 0) { + e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; +} + +void dfs(int root, int u, int step) { // root 表示根,u:当前走到哪个节点,step:到u点时走了几步 + st[u] = 1; // u走过了,防止回头路 + dis[root][u] = step, dis[u][root] = step; // root<->u之间的路径长度 + + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; + if (st[v]) continue; + dfs(root, v, step + 1); + } +} + +int main() { + // 初始化链式前向星 + memset(h, -1, sizeof h); + + cin >> n; + for (int i = 1; i <= n; i++) { + int a, b; + cin >> x[i] >> a >> b; + if (a) add(i, a), add(a, i); // 存图 + if (b) add(i, b), add(b, i); + } + for (int i = 1; i <= n; i++) { + memset(st, 0, sizeof st); + dfs(i, i, 0); // 搜索 + } + int ans = INF; + for (int i = 1; i <= n; i++) { + int s = 0; + for (int j = 1; j <= n; j++) + s = s + x[j] * dis[i][j]; // 累加距离 + ans = min(ans, s); + } + cout << ans << endl; + return 0; +} +``` + **三、$O(N)$算法**