前言
1.题目分析
本题目求解的是一个结点的子节点个数之和与除此之外的节点和最大值,与所有的结点的该属性对比,求出其中的最小值,类似于一个物体的重心。
这是比较典型的树的DFS,可以用数组建立无向图的树,从任意一个节点开始遍历它的邻边,并且通过布尔数组标记此节点,计算出此节点的子树节点总和,剩余节点值和为(n-子树节点总和),再用一个全局变量ans记录其中最大值中的最小值。
2.题解
2.1 建立无向图
const int N = 1e5+10;//数据范围
int e[2*N],ne[2*N],h[N],idx;
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
数组e存储结点的序号,ne数组存储指针关系,a->b,通过头插法接到a后面,再通过add(a,b),add(b,a)即可实现无向边。
2.2 遍历
bool st[N];
int ans = N, n;
int bfs(int u)
{
st[u] = true;
int res = 0, sum = 1; // res记录当前节点u的最大的子树或其他的节点和, sum记录u的子树之和
for(int i = h[u]; ~i; i = ne[i]) //遍历所有子节点
{
int j = e[i];
if(!st[j])
{
int s = dfs(j);
sum += s;
res = max(s,res);
}
}
res = max(res,n-sum); //其他节点之和用n-sum表示,补集的思想
ans = min(res,ans); //当前节点的最大值与其他所有的最大值比较,其中最小的会被记录下来
return sum; //返回子树之和,用于进行节点属性计算
}
3.代码呈现
#include<iostream>
#include<cstring>
#include<iostream>
using namespace std;
const int N = 1e5+10;
int e[2*N],ne[2*N],h[N],idx = 0;
bool st[N];
int n;
int ans = N;
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u)
{
st[u] = true;
int res = 0;
int sum = 1;
for(int i = h[u]; ~i ; i = ne[i])
{
int j = e[i];
if(!st[j])
{
int s = dfs(j);
sum += s;
res = max(s,res);
}
}
res = max(res,n-sum);
ans = min(res,ans);
return sum;
}
int main()
{
memset(h,-1,sizeof h); // 结点指向NULL(-1);
cin>>n;
for(int i = 0; i < n-1;i++)
{
int a,b; cin>>a>>b;
add(a,b),add(b,a);
}
dfs(1); //任意一点即可,因为无向加连通,但是需要数组st标记,不然会MLE;
cout<<ans<<endl;
}