知识:
定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。 (最大值的最小值)
树的重心的性质:
1.一个树最多只有1个或2个重心。如果有两个重心,它们必定相邻且在树的最长路径中间位置。
2.把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
3.树中所有点到某个点的距离和中,到重心的距离和是最小的;
4.把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
于是乎这个题就可以这样解决:
先把树的重心求出来, 算出所有其他结点到树的重心的距离之和。
树的重心的求法:
dfs遍历每一个点,求取每个点作为"重心“的最大子树的节点数,然后取最小的那个
作为重心。
具体看代码。
代码:
- #include
- #include
- #include
- #include
- using namespace std;
-
- const int N = 1e5 + 10;
- int n;
- vector<int> g[N];
- bool vis[N];
- int ans = 1 << 30;
- int dis[N], res;
- queue<int> q;
- int f[N];
-
- int dfs(int u) { // 返回值为子树的结点数
- vis[u] = true;
- int sum = 1; // 记录子树
- int size = 0; // 存放子树中节点数最大
-
- for(int i = 0; i < g[u].size(); i ++ ) {
- int j = g[u][i];
-
- if(!vis[j]) {
- int s = dfs(j);
- sum += s;
- size = max(size, s); // 比较所有下子树最大节点数
- }
-
- }
- ans = min(ans, max(size, n - sum)); // 和上子树比较求取最大子树结点数
- f[u] = max(size, n - sum); // f[u] 存放u结点最大子树结点数
-
- return sum;
- }
-
- void bfs(int start) {
- q.push(start);
- vis[start] = true;
-
- while(!q.empty()) {
- auto t = q.front();
- q.pop();
-
- for(int i = 0; i < g[t].size(); i ++ ) {
- int j = g[t][i];
- if(!vis[j]) {
- vis[j] = true;
- dis[j] = dis[t] + 1;
- q.push(j);
- }
- }
- }
- }
-
- int main() {
- cin >> n;
-
- for(int i = 1; i < n; i ++ ) {
- int a, b; cin >> a >> b;
-
- g[a].push_back(b);
- g[b].push_back(a);
- }
-
- dfs(1);
-
- memset(vis, false, sizeof vis);
-
- int st = N, start = 0;
-
- for(int i = 1; i <= n; i ++ ) { //比较每个点最大子树结点数, 若有两个重心取编号小的
- if(st > f[i]) {
- st = f[i];
- start = i;
- }
- else if(st == f[i]) {
- if(i < start) start = i;
- }
- }
-
- bfs(start); //bfs求距离和
-
- for(int i = 1; i <= n; i ++ ) {
- res += dis[i];
- }
-
-
- cout << start << ' ' << res << endl;
- return 0;
- }