题目
题目描述
小G想要把自己家院子里的橘子树搬到家门口(QAQ。。就当小G是大力水手吧)
可是小G是个平衡性灰常灰常差的人,他想找到一个这个橘子树的平衡点。
怎么描述这棵树呢。。。就把它看成由一个个节点构成的树吧。结点数就
代表树重。
输入描述
多组数据输入输出,
第一行包含一个整数n(3<=n<=1000)代表树的结点的个数
以下n-1行描述(1-n)节点间的连接关系。
输出描述
输出两个个整数 x,num 分别代表树的平衡点,和删除平衡点后最大子树的结点数(如果结点数相同输出编号小的)。
示例1
输入
3
1 2
1 3
输出
1 1
题解
知识点:树形dp。
要求的不是整颗树的性质,而是某点相对于树的性质,直接枚举每个点求一次dp复杂度太高,考虑二次扫描与换根法。
先取一个点作为根dp一遍,求出每个点的子树的答案。再从头开始进行dp,假设父节点对于整个树的答案已经求出,可以通过这个答案结合子树答案,推断出子节点对于整棵树的答案。
回到题目上,要求的是节点数。于是取 \(1\) 作为根节点,第一遍dp求出每个节点的子树的节点数 \(Size[u]\) ,转移方程看代码很基础。第二遍dp再从 \(1\) 出发求出节点答案 \(F[u]\)。考虑删除了父节点 \(u\) ,会出现由 \(u\) 的各个子节点 \(v_i\) 对应的子树,以及 \(u\) 向上的一整棵树。于是 \(F[u] = max(n-Size[u],Size[v_i])\) ,表示这些树中最多的节点数即是这个节点的答案。最终取 \(F[u]\) 里最小的,并记录编号即可。
时间复杂度 \(O(n)\)
空间复杂度 \(O(n)\)
代码
#include <bits/stdc++.h>
using namespace std;
int n;
vector<int> g[1007];
int Size[1007], F[1007];
int ans, id;
void dfs1(int u, int fa) {
for (auto v : g[u]) {
if (fa == v) continue;
dfs1(v, u);
Size[u] += Size[v];
}
Size[u]++;
}
void dfs2(int u, int fa) {
F[u] = n - Size[u];
for (auto v : g[u]) {
if (v == fa) continue;
dfs2(v, u);
F[u] = max(F[u], Size[v]);
}
if (ans > F[u]) {
ans = F[u];
id = u;
}
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
while (cin >> n) {
for (int i = 1;i <= n;i++) g[i].clear();
for (int i = 1;i <= n - 1;i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
memset(Size, 0, sizeof(Size));
dfs1(1, 0);
ans = 1e9;
dfs2(1, 0);
cout << id << ' ' << ans << '\n';
}
return 0;
}
标签:子树,大树,fa,一个,int,NC15033,节点,dp,Size
From: https://www.cnblogs.com/BlankYang/p/16615709.html