经典树形DP问题:树的重心,树的中心,树的直径
树形DP经典问题
树的重心
树的重心:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e6 + 2049, M = 2049;
const int INF = 0x3f3f3f3f;
const int Mod = 1e9 + 7;
int read() {
int x = 0, f = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return f ? -x : x;
}
void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + 48);
}
int n, w[N], Size[N], head[N], cnt, Min = INF, Id;
;
struct node {
int v, nxt;
} e[N];
void Add_edge(int u, int v) {
e[++cnt] = (node){v, head[u]};
head[u] = cnt;
}
void dfs(int u, int fa) {
Size[u] = 1;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa) continue;
dfs(v, u);
Size[u] += Size[v];
w[u] = max(w[u], Size[v]);
//处理出每个点子树的大小
}
w[u] = max(w[u], n - Size[u]); //子树的最大值为下方子树和上方子树取max
if (w[u] < Min) Min = w[u], Id = u; //更新
}
void clear() {
memset(head, 0, sizeof head);
memset(Size, 0, sizeof Size);
memset(w, 0, sizeof w);
cnt = 0;
Min = INF;
Id = 0;
return;
}
signed main() {
int T = read();
for (; T; T--) {
n = read();
clear();
for (int i = 1, u, v; i < n; i++) {
u = read();
v = read();
Add_edge(u, v);
Add_edge(v, u);
}
dfs(1, 0);
print(Id);
putchar(' ');
print(Min);
putchar('\n');
}
return 0;
}
树的中心
树的中心:到每一个点的距离的最大值最小的点叫树的中心。
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e6 + 2049, M = 2049;
const int INF = 0x3f3f3f3f;
const int Mod = 1e9 + 7;
int read() {
int x = 0, f = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return f ? -x : x;
}
void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + 48);
}
int n, head[N], cnt;
struct node {
int v, w, nxt;
} e[N];
void Add_edge(int u, int v, int w) {
e[++cnt] = (node){v, w, head[u]};
head[u] = cnt;
}
int d1[N], d2[N], c1[N], c2[N]; //最远次远以及更新他们 的子节点
void dfs1(int u, int fa) {
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v, w = e[i].w;
if (v == fa) continue;
dfs1(v, u); //由下而上更新
if (d1[v] + w > d1[u]) {
d2[u] = d1[u];
c2[u] = c1[u];
d1[u] = d1[v] + w;
c1[u] = v; //和树的最长路径一样,多了个记录
} else if (d1[v] + w > d2[u]) {
d2[u] = d1[v] + w;
c2[u] = v;
}
}
return;
}
int up[N]; //向上能走的最远距离。
//更新向上走的距离,但实际是向下走的。
void dfs2(int u, int fa) {
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa) continue;
if (c1[u] ^ v)
up[v] =
max(up[u], d1[u]) + e[i].w; //不位于最远路径上,由父节点的最远更新
else
up[v] =
max(up[u], d2[u]) + e[i].w; //位于最短路径上,由父节点的次远更新。
dfs2(v, u);
}
return;
}
signed main() {
n = read();
for (int i = 1, u, v, w; i < n; i++) {
u = read();
v = read();
w = read();
Add_edge(u, v, w);
Add_edge(v, u, w);
}
dfs1(1, 0);
dfs2(1, 0);
int Ans = INF, Id;
for (int i = 1; i <= n; i++) {
if (max(d1[i], up[i]) < Ans) Ans = max(d1[i], up[i]), Id = i;
} //取向上向下的最大值,再取min
print(Id);
putchar(' ');
print(Ans);
return 0;
}
树的直径
树的最长路径:一条路径,使得路径两端的点的距离最远。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e6 + 2049, M = 2049;
const int INF = 0x3f3f3f3f;
const int Mod = 1e9 + 7;
int read() {
int x = 0, f = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return f ? -x : x;
}
void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + 48);
}
int n, Ans, head[N], cnt;
struct node {
int v, w, nxt;
} e[N];
void Add_edge(int u, int v, int w) {
e[++cnt] = (node){v, w, head[u]};
head[u] = cnt;
}
int dfs(int u, int fa) {
int d1 = 0, d2 = 0;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa) continue;
int d = dfs(v, u) + e[i].w; //向下走的最远距离
if (d >= d1)
d2 = d1, d1 = d; //当前距离比之前的最远远,原最远变次远,更新最远
else if (d > d2)
d2 = d; //如果不能更新最远但是能更新次远,更新。
}
Ans = max(Ans, d1 + d2); //更新路径
return d1;
}
signed main() {
n = read();
for (int i = 1, u, v, w; i < n; i++) {
u = read();
v = read();
w = read();
Add_edge(u, v, w);
Add_edge(v, u, w);
}
dfs(1, 0);
print(Ans);
return 0;
}
由于全网公开的 OJ 都没有测评(唯一的测评Acwing要收费),所以代码没有经过测试检验,可能会有漏洞,如有错误请指正。