在刚开始准备夏令营机试的时候我就看过树形dp,但是当时由于自己的孱弱被吓到了,一直到今天又遇到了类似的题才真正重新学了一遍,其实过程很简单,就是一遍简单的dfs,在过程中记录参数而已
题目大意
题目链接
情景说的有点复杂,但其实就是让你找出这棵树所有的重心节点。
思路分析
首先,我们了解下什么是树的重心:
对于一棵n个节点无根树,若有一点,当令该点为根的时候,最大节点数最少。也可以说成:删除这个点之后,各连通块的节点数最少,则这一点就是树的重心。
了解完重心的定义之后,我们再来看下怎么求出重心:
我的做法是:先以节点1为根,dfs一遍这棵树,然后需要记下两个信息:一个是这棵树的子结点数量,还有一个就是这棵树的子树中最大的节点数量。
我们以样例为例:
样例的树如下:
对于节点3:它的儿子数是7,子树中最大的节点数就是5(8所在的子树上的系欸桉树)。
对于节点1:它的儿子数是9,子树中最大的节点数也是9(因为1只有一个子树)
dfs一次之后,我们就可以根据这些信息判断。
对于某一个节点,让他成为根之后,它的子树中节点的最大值有两种可能:一种是刚才dfs出来的结果中的子树中最大的节点数,另一种是刚才dfs过程中没有记录的它的根及其以上的部分的节点数。
依旧举个栗子:
对于3这个节点,子树中最大的节点数就是5,它的根及其以上的部分就是2和1。
对于7这个节点,子树中最大的节点数就是1,它的根及其以上的部分就是剩下的1,2,3,4,5,8,9,10。
我们要找的这个节点其实就是这两个部分的值都小于整个树的节点数的一半(n/2)的节点。
那么,怎么算出它的根及其以上的部分的节点数?可以直接n-这个点的儿子数-1(它自己)。
#define ll long long
#define inf 0x3f3f3f3f
#define vec vector<int>
#define P pair<int,int>
#define MAX 10005
vec G[MAX];
int N, x, y, sons[MAX], maxs[MAX];
void dfs(int fa, int u) {
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (fa == v)continue;
dfs(u, v);
sons[u] += sons[v] + 1;//记录总共的节点
maxs[u] = max(maxs[u], sons[v] + 1);//记录最多节点的孩子
}
}
int main() {
while (scanf("%d", &N) != EOF) {
for (int i = 1; i <= N; i++)G[i].clear();
for (int i = 1; i < N; i++) {
scanf("%d %d", &x, &y);
G[x].push_back(y); G[y].push_back(x);
}
memset(sons, 0, sizeof(0));
memset(maxs, 0, sizeof(maxs));
dfs(-1, 1);
int sign = 0;
for (int i = 1; i <= N; i++)
if (maxs[i] <= N / 2 && N - sons[i] - 1 <= N / 2)
cout << i << endl, sign = 1;
if (!sign)cout << "NONE" << endl;
}
}