经典树形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要收费),所以代码没有经过测试检验,可能会有漏洞,如有错误请指正。

posted @ 2022-03-19 21:13  Gym_nastics  阅读(71)  评论(0编辑  收藏  举报