动态规划:树的重心 树形DP

树的重心 
【题目】
  给定一棵树,树中包括n个结点(编号1-n),和n-1条无向边。
  请你找到树的重心,并输出将重心删除后,各个联通块中 结点数的最大值。
  重心:是指树中的一个结点,将这个点删除后,剩余各个联通块中 结点数 的最大值 最小 那么这个点就被称为树的重心。
  1<=n<=1e5
 
 
【思路】:
  先构建一棵树,从某一个结点出发,求出这个结点联通块中最大的子树,我们可以画图分析

 

 对于一个结点u 我们要求他联通块的最大size。可以从u点往下搜,设三个子树为a b c 那么最大联通块就是max(a,b,c);但这其实考虑是不够的,我们还要考虑u上面连得子树,因为一个u结点肯定也只是从上面搜下来的,所以u结点上面只会有一棵树。上面树的size其实就是 n就是总节点数 n-1-(a+b+c)就是用总节点数减去u这个子树的节点数,然后求这a b c up 的size的max,更新ans即可,每一次dfs返回值一定要返回sum(所以子树) 这样对于u搜到 a a再往下搜,那么a往下搜返回的就是a这棵树的size。dfs的过程中 因为是从上往下的搜索,既然是无向边,可以任意选一个点为根节点往下搜,注意要构建bool vis数组,每一次搜到这个结点是标记一下,避免向上重复搜索。

就得到关键dfs代码:

  

 

 

总代码:

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N = 10010, M = N * 2;
 5 int n, ans=N;//ans记录最长路径的长度 要初始化为大一点
 6 bool vis[N];//记录是否被搜索过
 7 int head[N], to[M], nexts[M], index;//邻接表
 8 void add(int a, int b)//邻接表存储
 9 {
10     to[++index] = b;
11     nexts[index] = head[a];
12     head[a] = index;
13 }
14 int dfs(int u)
15 {
16     vis[u] = true;//代表这点已经搜过
17     int size = 0;//记录u的最大子树的节点数
18     int sum = 1;//记录以u为根的子树的节点数
19     for (int i = head[u]; i != -1; i = nexts[i])//邻接表
20     {
21         int j = to[i];//j是u的邻界点
22         if (vis[j])
23             continue;//避免向上查找
24         int s = dfs(j);//s是以j为根的节点数
25         size = max(s, size);//记录u的最大子树的节点数
26         sum += s;//累加各个子树的结点数
27     }
28     ans = min(ans, max(size, n - sum));//更新答案
29     return sum;
30 }
31 int main()
32 {
33     memset(head, -1, sizeof(head));
34     cin >> n;
35     for (int i = 1; i < n; ++i)
36     {
37         int a, b;
38         cin >> a >> b;
39         add(a, b);
40         add(b, a);
41     }
42     dfs(1);//任取一个点 开始深搜
43     cout << ans << endl;
44     return 0;
45 }

 

 

posted @ 2022-04-27 10:37  朱朱成  阅读(82)  评论(0编辑  收藏  举报