NC15033 小G有一个大树

题目链接

题目

题目描述

小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;
}
posted @ 2022-08-23 12:14  空白菌  阅读(58)  评论(0编辑  收藏  举报