树的直径与重心
Posted lifehappy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树的直径与重心相关的知识,希望对你有一定的参考价值。
树的直径与重心
树的直径求解方法一
思路
先选取一个点rt作为根节点,dfs去找到一个最长路径的点U,然后通过这个点去dfs,找到路径最长的点V,U->V就是这课树的直径。
证明正确性:
假如rt在直径上的话,最长路径的点U一定是直径的一个端点,这一点是显然的。
假如rt不在直径上,那么从这个点出发也一定可以找到一条路到达直径上一点,接下来就如同上面一样了,无非就是在真正的dis上再加上一段固定的value,对我们最后的直径端点查找并无影响。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int head[N], to[N], value[N], nex[N], cnt;
int n, ans, dis[N];
inline ll read() {
ll x = 1, s = 0; char c;
c = getchar();
while(c < ‘0‘ || c > ‘9‘) {
if(c == ‘-‘) x = -1;
c = getchar();
}
while(c >= ‘0‘ && c <= ‘9‘) {
s = (s << 1) + (s << 3) + (c ^ 48);
c = getchar();
}
return x * s;
}
void add(int x, int y, int w) {
to[cnt] = y;
nex[cnt] = head[x];
value[cnt] = w;
head[x] = cnt++;
}
void dfs(int rt, int fa) {
for(int i = head[rt]; ~i; i = nex[i]) {
if(to[i] == fa) continue;
dfs(to[i], rt);
dis[to[i]] = dis[rt] + value[i];
}
}
int main() {
n = read();
int x, y, w;
memset(head, -1, sizeof head);
for(int i = 1; i < n; i++) {
x = read(), y = read(), w = read();
add(x, y, w);
add(y, x, w);
}
dfs(1, 0);
int u = 0, v = 0;
for(int i = 1; i <= n; i++)
if(dis[i] > dis[u]) u = i;
memset(dis, 0, sizeof dis);
dfs(u, 0);
for(int i = 1; i <= n; i++)
if(dis[i] > dis[v]) v = i;
printf("%d %d %d
", u, v, dis[v]);
return 0;
}
树的直径求解方法二
思路
以根节点出发,去寻找到这个根节点的最长路,以及次长路,得到的两个节点就是树的直径的端点。如果我们要得到直径的数值,还应该要做一次dfs,因为我们一开始选定的根节点无法保证是不是在直径上。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int head[N], to[N], value[N], nex[N], cnt;
int n, ans, fir[N], sec[N], firnode[N], secnode[N];
inline ll read() {
ll x = 1, s = 0; char c;
c = getchar();
while(c < ‘0‘ || c > ‘9‘) {
if(c == ‘-‘) x = -1;
c = getchar();
}
while(c >= ‘0‘ && c <= ‘9‘) {
s = (s << 1) + (s << 3) + (c ^ 48);
c = getchar();
}
return x * s;
}
void add(int x, int y, int w) {
to[cnt] = y;
nex[cnt] = head[x];
value[cnt] = w;
head[x] = cnt++;
}
void dfs(int rt, int fa) {
firnode[rt] = secnode[rt] = rt;
for(int i = head[rt]; ~i; i = nex[i]) {
if(to[i] == fa) continue;
dfs(to[i], rt);
if(fir[to[i]] + value[i] > fir[rt]) {//更新最长路
sec[rt] = fir[rt];
fir[rt] = fir[to[i]] + value[i];
firnode[rt] = to[i];
}
else if(fir[to[i]] + value[i] > sec[rt]) {//更新次长路
sec[rt] = fir[to[i]] + value[i];
secnode[rt] = to[i];
}
}
}
int main() {
n = read();
int x, y, w;
memset(head, -1, sizeof head);
for(int i = 1; i < n; i++) {
x = read(), y = read(), w = read();
add(x, y, w);
add(y, x, w);
}
dfs(1, 0);
printf("%d %d %d", firnode[1], secnode[1], fir[1] + sec[1]);//假定我们选择的点是直径上的点。
return 0;
}
树的重心
思路
找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样,则这两个点都是重心
并且,一棵树最多有两个重心,且相邻。
因此我们可以通过dfs去记录每个节点的子树节点值,然后去统计其最大结点数最小的节点。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int head[N], to[N], value[N], nex[N], cnt;
int n, ans, sz[N], maxn = 0x3f3f3f3f, heart;
inline ll read() {
ll x = 1, s = 0; char c;
c = getchar();
while(c < ‘0‘ || c > ‘9‘) {
if(c == ‘-‘) x = -1;
c = getchar();
}
while(c >= ‘0‘ && c <= ‘9‘) {
s = (s << 1) + (s << 3) + (c ^ 48);
c = getchar();
}
return x * s;
}
void add(int x, int y, int w) {
to[cnt] = y;
nex[cnt] = head[x];
value[cnt] = w;
head[x] = cnt++;
}
void dfs(int rt, int fa) {
sz[rt] = 1;
int now_maxn = 0;
for(int i = head[rt]; ~i; i = nex[i]) {
if(to[i] == fa) continue;
dfs(to[i], rt);
sz[rt] += sz[to[i]];
if(sz[to[i]] > now_maxn) now_maxn = sz[to[i]];//记录最大子节点。
}
if(n - sz[rt] > now_maxn) now_maxn = n - sz[rt];//其父节点也算是他的子节点
if(now_maxn < maxn) heart = rt, maxn = now_maxn;
}
int main() {
n = read();
int x, y, w;
memset(head, -1, sizeof head);
for(int i = 1; i < n; i++) {
x = read(), y = read(), w = read();
add(x, y, w);
add(y, x, w);
}
dfs(1, 0);
printf("%d
", heart);
return 0;
}
以上是关于树的直径与重心的主要内容,如果未能解决你的问题,请参考以下文章