树的直径
树上任意两节点之间最长的简单路径即为树的直径
两次DFS
若存在负权边,则无法使用两次DFS的方式求解直径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const int N = 10000 + 10;
int n, c, d[N]; vector<int> E[N];
void dfs(int u, int fa) { for (int v : E[u]) { if (v == fa) continue; d[v] = d[u] + 1; if (d[v] > d[c]) c = v; dfs(v, u); } }
int main() { scanf("%d", &n); for (int i = 1; i < n; i++) { int u, v; scanf("%d %d", &u, &v); E[u].push_back(v), E[v].push_back(u); } dfs(1, 0); d[c] = 0, dfs(c, 0); printf("%d\n", d[c]); return 0; }
|
如果需要求出一条直径上所有节点,则可以在第二次DFS的过程中,记录每个点的前序节点,即可从直径的一端一路向前,遍历直径上所有的节点
树形dp
实现1
d1
和d2
分别为每个节点作为字树的根向下,所能延伸的最长路径长度d1
与此长路径d2
,那么直径就是对于每个点,该点d1+d2
所能取到的值中的最大值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const int N = 10000 + 10;
int n, d = 0; int d1[N], d2[N]; vector<int> E[N];
void dfs(int u, int fa) { d1[u] = d2[u] = 0; for (int v : E[u]) { if (v == fa) continue; dfs(v, u); int t = d1[v] + 1; if (t > d1[u]) d2[u] = d1[u], d1[u] = t; else if (t > d2[u]) d2[u] = t; } d = max(d, d1[u] + d2[u]); }
int main() { scanf("%d", &n); for (int i = 1; i < n; i++) { int u, v; scanf("%d %d", &u, &v); E[u].push_back(v), E[v].push_back(u); } dfs(1, 0); printf("%d\n", d); return 0; }
|
实现2
dp[u]
:以u为根的子树中,从u出发的最长路径。
转移方程:dp[u] = max(dp[u],dp[v]+w(u,v)) , w(u,v)表示所经过边的权重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const int N = 10000 + 10;
int n, d = 0; int dp[N]; vector<int> E[N];
void dfs(int u, int fa) { for (int v : E[u]) { if (v == fa) continue; dfs(v, u); d = max(d, dp[u] + dp[v] + 1); dp[u] = max(dp[u], dp[v] + 1); } }
int main() { scanf("%d", &n); for (int i = 1; i < n; i++) { int u, v; scanf("%d %d", &u, &v); E[u].push_back(v), E[v].push_back(u); } dfs(1, 0); printf("%d\n", d); return 0; }
|