Balancing Act

题目描述

对于一个树T,其中n(1<=n<=20000)个节点编号为1…n。

从树中删除任何节点都会生成一个林:一个或多个树的集合。

将一个节点的平衡值定义为从T中删除该节点、创建的林中最大树的结点数。

例如,对于树:

删除节点4会生成两个树,其成员节点分别为5和1、2、3、6、7,这两棵树中较大的有五个节点,因此节点4的平衡值为5。

删除节点1会生成三棵大小相同的树的林:2、6,3、7和4、5,每个树都有两个节点,因此节点1的平衡值是2。

对于每个输入树,计算具有最小平衡值的节点。如果多个节点具有相等的平衡,则输出数字最小的节点。

输入格式

输入的第一行包含一个整数t(1<=t<=20),即测试用例的数量。

每个测试用例的第一行包含一个整数n(1<=n<=20000),接下来的n-1行每行包含两个空格分隔的节点号,这些节点号是树中一条边的端点,不会列出两次边,所有边都将列出。

输出格式

对于每个测试用例,打印一行包含两个整数,最小平衡值的节点编号 和 该节点的平衡值。

输入输出样例

输入样例1:复制
1
7
2 6
1 2
1 4
4 5
3 7
3 1
输出样例1:复制
1 2

【耗时限制】1000ms 【内存限制】64MB


问题目标是删除一个结点后,最大连通块的结点数最少,即找树的重心(质心)。

状态定义:任选一个节点为根,设d(i)表示以i为根节点的子树的节点个数。

状态转移方程: d(i)= sum(d(i)) ; j∈ son(i) ; 删除结点后最大连通块中结点数:f(i)= max(n-d(i),max (d(j));j∈son(i) ;

问题答案:ans=𝒎in(f(i))&&i编号最小。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <sstream>
#include <vector>
using namespace std;
int t,n,d[20005];
vector<int> G[20005];
int ans,id;
void dfs(int u,int fa){
    int maxn=0;
    d[u]=1;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v!=fa){
            dfs(v,u);
            maxn=max(maxn,d[v]);
            d[u]+=d[v];
        }
    }
    maxn=max(maxn,n-d[u]);
    if(maxn<ans||maxn==ans&&id>u) id=u,ans=maxn;
}
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        ans=0x3f3f3f3f;
        id=0x3f3f3f3f;
        memset(d,0,sizeof(d));
        for(int i=1;i<=n;i++) G[i].clear();
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1,-1);
        printf("%d %d\n",id,ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值