diff --git a/TangDou/AcWing/Tree/1073.md b/TangDou/AcWing/Tree/1073.md index 2cb1764..44e0f07 100644 --- a/TangDou/AcWing/Tree/1073.md +++ b/TangDou/AcWing/Tree/1073.md @@ -43,11 +43,10 @@ $1≤c_i≤10^5$ using namespace std; const int INF = 0x3f3f3f3f; -const int N = 10010, M = 20010; +const int N = 10010, M = N << 1; int n; int ans; int res = INF; -int st[N]; // 邻接表 int e[M], h[N], idx, w[M], ne[M]; @@ -55,14 +54,13 @@ 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 u, int sum) { - st[u] = 1; +void dfs(int u, int fa, int sum) { for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i]; - if (st[j]) continue; - dfs(j, sum + w[i]); + int v = e[i]; + if (v == fa) continue; + dfs(v, u, sum + w[i]); } - if (sum > ans) ans = sum; + ans = max(ans, sum); } int main() { @@ -73,10 +71,10 @@ int main() { cin >> a >> b >> c; add(a, b, c), add(b, a, c); } + // 暴力换根 for (int i = 1; i <= n; i++) { ans = 0; - memset(st, 0, sizeof st); - dfs(i, 0); + dfs(i, 0, 0); res = min(res, ans); } printf("%d\n", res); @@ -92,7 +90,7 @@ int main() { 同样,先来想一下如何暴力求解该问题:先 **枚举** 目标节点,然后求解该节点到其他节点的 **最远距离** -时间复杂度为 $O(n^2)$,对于本题的 **数据规模**,十分极限,经测试只能过 $6/10$ +时间复杂度为 $O(n^2)$,对于本题的 **数据规模**,十分极限,经测试只能过 $7/11$ #### 考虑如何优化求解该问题的方法 思考一下:在确定树的 **拓扑结构** 后单独求一个节点的 **最远距离** 时,会在该树上去比较哪些 **路径** 呢? @@ -139,17 +137,15 @@ $up[u]$:存下$u$节点向上走的最长路径的长度 #include using namespace std; -const int N = 10010; -const int M = N << 1; +const int N = 10010, M = N << 1; const int INF = 0x3f3f3f3f; -int n; +int n; // n个节点 +int mx1[N]; // mx1[u]:u节点向下走的最长路径的长度 +int mx2[N]; // mx2[u]:u节点向下走的次长路径的长度 +int id[N]; // id[u]:u节点向下走的最长路径是从哪一个节点下去的 +int up[N]; // up[u]:u节点向上走的最长路径的长度 -int d1[N]; // d1[u]:存下u节点向下走的最长路径的长度 -int d2[N]; // d2[u]:存下u节点向下走的次长路径的长度 -int p1[N]; // p1[u]:存下u节点向下走的最长路径是从哪一个节点下去的 -int up[N]; // up[u]:存下u节点向上走的最长路径的长度 -int st[N]; // 邻接表 int e[M], h[N], idx, w[M], ne[M]; void add(int a, int b, int c = 0) { @@ -157,39 +153,35 @@ void add(int a, int b, int c = 0) { } // 功能:以u为根,向叶子进行递归,利用子节点返回的最长信息,更新自己的最长和次长,并记录最长是从哪个节点来的 -void dfs1(int u) { - st[u] = 1; +void dfs1(int u, int fa) { for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i]; - if (st[j]) continue; + int v = e[i]; + if (v == fa) continue; // 递归完才能有数据 - dfs1(j); - - if (d1[j] + w[i] >= d1[u]) { // 更新最长 - d2[u] = d1[u]; // ① 更新次长,必须在第一位,因为下面d1[u]会被改写 - d1[u] = d1[j] + w[i]; // ② 更新最长 - p1[u] = j; // ③ 记录最长来源 - } else if (d1[j] + w[i] > d2[u]) // 更新次长 - d2[u] = d1[j] + w[i]; + dfs1(v, u); + int x = mx1[v] + w[i]; // u问到:儿子v可以带我走多远? + if (mx1[u] < x) { // 更新最长 + mx2[u] = mx1[u]; // ① 更新次长 + mx1[u] = x; // ② 更新最长 + id[u] = v; // ③ 记录最长来源 + } else if (mx2[u] < x) // 更新次长 + mx2[u] = x; } } // 功能:完成向上的信息填充 -void dfs2(int u) { - st[u] = 1; +void dfs2(int u, int fa) { for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i]; - if (st[j]) continue; - - // 三者取其一 - up[j] = w[i] + up[u]; - if (p1[u] == j) - up[j] = max(up[j], w[i] + d2[u]); + int v = e[i]; + if (v == fa) continue; + // 二者取其一 + if (id[u] == v) + up[v] = max(mx2[u], up[u]) + w[i]; else - up[j] = max(up[j], w[i] + d1[u]); - // 准备好了信息,再进入递归 - dfs2(j); + up[v] = max(mx1[u], up[u]) + w[i]; + // 递归 + dfs2(v, u); } } @@ -201,13 +193,11 @@ int main() { cin >> a >> b >> c; add(a, b, c), add(b, a, c); } - memset(st, 0, sizeof st); - dfs1(1); // 选择任意一个节点进行dfs,用儿子更新父亲的统计信息 - memset(st, 0, sizeof st); - dfs2(1); // 向上 + dfs1(1, 0); // 选择任意一个节点进行dfs,用儿子更新父亲的统计信息 + dfs2(1, 0); // 向上 int res = INF; - for (int i = 1; i <= n; i++) res = min(res, max(d1[i], up[i])); + for (int i = 1; i <= n; i++) res = min(res, max(mx1[i], up[i])); printf("%d\n", res); return 0; @@ -247,6 +237,3 @@ void dfs1(int u) { // if (d1[u] == -INF) d1[u] = d2[u] = 0; //特判叶子结点 } ``` -### 四、下一步需研读 -https://blog.csdn.net/weixin_44232130/article/details/116567482 -https://www.cnblogs.com/hxxO-o/p/16558801.html diff --git a/TangDou/AcWing/Tree/1073_1.cpp b/TangDou/AcWing/Tree/1073_1.cpp index e28a93f..d77da0a 100644 --- a/TangDou/AcWing/Tree/1073_1.cpp +++ b/TangDou/AcWing/Tree/1073_1.cpp @@ -2,11 +2,10 @@ using namespace std; const int INF = 0x3f3f3f3f; -const int N = 10010, M = 20010; +const int N = 10010, M = N << 1; int n; int ans; int res = INF; -int st[N]; // 邻接表 int e[M], h[N], idx, w[M], ne[M]; @@ -14,14 +13,13 @@ 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 u, int sum) { - st[u] = 1; +void dfs(int u, int fa, int sum) { for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i]; - if (st[j]) continue; - dfs(j, sum + w[i]); + int v = e[i]; + if (v == fa) continue; + dfs(v, u, sum + w[i]); } - if (sum > ans) ans = sum; + ans = max(ans, sum); } int main() { @@ -32,10 +30,10 @@ int main() { cin >> a >> b >> c; add(a, b, c), add(b, a, c); } + // 暴力换根 for (int i = 1; i <= n; i++) { ans = 0; - memset(st, 0, sizeof st); - dfs(i, 0); + dfs(i, 0, 0); res = min(res, ans); } printf("%d\n", res); diff --git a/TangDou/AcWing/Tree/1073_2.cpp b/TangDou/AcWing/Tree/1073_2.cpp index 7369129..c2204c2 100644 --- a/TangDou/AcWing/Tree/1073_2.cpp +++ b/TangDou/AcWing/Tree/1073_2.cpp @@ -1,17 +1,15 @@ #include using namespace std; -const int N = 10010; -const int M = N << 1; +const int N = 10010, M = N << 1; const int INF = 0x3f3f3f3f; -int n; +int n; // n个节点 +int mx1[N]; // mx1[u]:u节点向下走的最长路径的长度 +int mx2[N]; // mx2[u]:u节点向下走的次长路径的长度 +int id[N]; // id[u]:u节点向下走的最长路径是从哪一个节点下去的 +int up[N]; // up[u]:u节点向上走的最长路径的长度 -int d1[N]; // d1[u]:存下u节点向下走的最长路径的长度 -int d2[N]; // d2[u]:存下u节点向下走的次长路径的长度 -int p1[N]; // p1[u]:存下u节点向下走的最长路径是从哪一个节点下去的 -int up[N]; // up[u]:存下u节点向上走的最长路径的长度 -int st[N]; // 邻接表 int e[M], h[N], idx, w[M], ne[M]; void add(int a, int b, int c = 0) { @@ -19,39 +17,35 @@ void add(int a, int b, int c = 0) { } // 功能:以u为根,向叶子进行递归,利用子节点返回的最长信息,更新自己的最长和次长,并记录最长是从哪个节点来的 -void dfs1(int u) { - st[u] = 1; +void dfs1(int u, int fa) { for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i]; - if (st[j]) continue; + int v = e[i]; + if (v == fa) continue; // 递归完才能有数据 - dfs1(j); - - if (d1[j] + w[i] >= d1[u]) { // 更新最长 - d2[u] = d1[u]; // ① 更新次长,必须在第一位,因为下面d1[u]会被改写 - d1[u] = d1[j] + w[i]; // ② 更新最长 - p1[u] = j; // ③ 记录最长来源 - } else if (d1[j] + w[i] > d2[u]) // 更新次长 - d2[u] = d1[j] + w[i]; + dfs1(v, u); + int x = mx1[v] + w[i]; // u问到:儿子v可以带我走多远? + if (mx1[u] < x) { // 更新最长 + mx2[u] = mx1[u]; // ① 更新次长 + mx1[u] = x; // ② 更新最长 + id[u] = v; // ③ 记录最长来源 + } else if (mx2[u] < x) // 更新次长 + mx2[u] = x; } } // 功能:完成向上的信息填充 -void dfs2(int u) { - st[u] = 1; +void dfs2(int u, int fa) { for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i]; - if (st[j]) continue; - - // 三者取其一 - up[j] = w[i] + up[u]; - if (p1[u] == j) - up[j] = max(up[j], w[i] + d2[u]); + int v = e[i]; + if (v == fa) continue; + // 二者取其一 + if (id[u] == v) + up[v] = max(mx2[u], up[u]) + w[i]; else - up[j] = max(up[j], w[i] + d1[u]); - // 准备好了信息,再进入递归 - dfs2(j); + up[v] = max(mx1[u], up[u]) + w[i]; + // 递归 + dfs2(v, u); } } @@ -63,13 +57,11 @@ int main() { cin >> a >> b >> c; add(a, b, c), add(b, a, c); } - memset(st, 0, sizeof st); - dfs1(1); // 选择任意一个节点进行dfs,用儿子更新父亲的统计信息 - memset(st, 0, sizeof st); - dfs2(1); // 向上 + dfs1(1, 0); // 选择任意一个节点进行dfs,用儿子更新父亲的统计信息 + dfs2(1, 0); // 向上 int res = INF; - for (int i = 1; i <= n; i++) res = min(res, max(d1[i], up[i])); + for (int i = 1; i <= n; i++) res = min(res, max(mx1[i], up[i])); printf("%d\n", res); return 0; diff --git a/TangDou/Topic/【换根DP】专题.md b/TangDou/Topic/【换根DP】专题.md index 90b70a5..c9aac9a 100644 --- a/TangDou/Topic/【换根DP】专题.md +++ b/TangDou/Topic/【换根DP】专题.md @@ -1193,7 +1193,9 @@ signed main() { for (int i = 1; i <= n; i++) cout << g[i] - max(up[i], mx1[i]) << endl; } ``` +#### [$AcWing$ $1073$. 树的中心](https://www.cnblogs.com/littlehb/p/15786805.html) +#### [$AcWing$ $1148$ 秘密的牛奶运输](https://www.cnblogs.com/littlehb/p/16054005.html) #### [$CF708C$ $Centroids$](https://www.luogu.com.cn/problem/CF708C) @@ -1208,6 +1210,3 @@ https://www.cnblogs.com/DongPD/p/17498336.html https://blog.csdn.net/Emm_Titan/article/details/123875298 -#### [$AcWing$ $1073$. 树的中心](https://www.cnblogs.com/littlehb/p/15786805.html) - -#### [$AcWing$ $1148$ 秘密的牛奶运输](https://www.cnblogs.com/littlehb/p/16054005.html) \ No newline at end of file