树的重心:树的重心也叫树的质心。找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
POJ 3107 http://poj.org/problem?id=3107
两个都是模板题,主要还是对树的重心的理解。简单的DFS的应用,记录每次删除当前结点之后每个子树的最大节点数,最小化最大节点数就是树的重心,在一棵树中,树的重心可能不止一个。
换句话说,删除这个点后最大连通块(一定是树)的结点数最小。
eg:
删除1:子树1:2、6 子树2:4、5 子树3:3、7 ans[1]=2;
删除2:子树1:6 子树2:1、3、4、5、7 ans[2]=5;
删除3:子树1:7 子树2:1、2、4、5、6 ans[3]=5;
删除4:子树1:5 子树2:1、2、3、6、7 ans[4]=5;
删除5:子树1:1、2、3、4、6、7 ans[5]=6;
删除6:子树1:1、2、3、4、5、7 ans[6]=6;
删除7:子树1:1、2、3、4、5、6 ans[7]=6;
因此,树的重心为cnt=min(ans[i],cnt)=2;
题目大意:给定一棵树,求树的重心的编号以及重心删除后得到的最大子树的节点个数size,如果有多个重心即size相同就选取编号最小的。
CODE:
const int maxn=500005;
int tot=0,n;
int ans,size;
int sx[maxn],head[maxn];
int vis[maxn];
struct edge
{
int to,next;
} eg[maxn];
void add(int u,int v)
{
eg[tot].to=v;
eg[tot].next=head[u];
head[u]=tot++;
}
void init()
{
tot=0;
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
}
void dfs(int u)
{
vis[u]=1;
sx[u]=1;
int tmp=0;
for(int i=head[u]; i!=-1; i=eg[i].next)
{
int v=eg[i].to;
if(!vis[v])
{
dfs(v);
sx[u]+=sx[v];
tmp=max(tmp,sx[v]);
}
}
tmp=max(tmp,n-sx[u]);
if(size>tmp||size==tmp&&ans>u)
{
ans=u;
size=tmp;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
init();
int u,v;
scanf("%d",&n);
for(int i=1; i<n; i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
size=INF;
dfs(1);
printf("%d %d\n",ans,size);
}
}
POJ 3107 http://poj.org/problem?id=3107
题目大意:给定一棵树,求树的所有重心,按照编号从小到大的顺序输出。
CODE:
const int maxn=100005;
struct node
{
int to,next;
} eg[maxn];
int tot=0,num=0,n;
int vis[maxn],sx[maxn],ans[maxn];
int head[maxn];
void add(int u,int v)
{
eg[tot].to=v;
eg[tot].next=head[u];
head[u]=tot++;
}
int size,cnt;
void dfs(int u)
{
vis[u]=1;
sx[u]=1;
int tmp=0;
for(int i=head[u]; i!=-1; i=eg[i].next)
{
int v=eg[i].to;
if(!vis[v])
{
dfs(v);
sx[u]+=sx[v];
tmp=max(tmp,sx[v]);
}
}
tmp=max(tmp,n-sx[u]);
if(tmp<size)
{
//printf("&&&&&&\n");
num=0;
size=tmp;
ans[num++]=u;
}
else if(tmp==size)
{
//printf("*********\n");
ans[num++]=u;
}
}
void init()
{
tot=0,num=0;
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
memset(ans,0,sizeof(ans));
size=INF;
}
int main()
{
while(~scanf("%d",&n))
{
init();
int u,v;
for(int i=1; i<n; i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dfs(1);
sort(ans,ans+num);
for(int i=0; i<num; i++)
{
if(i)
printf(" ");
printf("%d",ans[i]);
}
printf("\n");
}
return 0;
}
两个都是模板题,主要还是对树的重心的理解。简单的DFS的应用,记录每次删除当前结点之后每个子树的最大节点数,最小化最大节点数就是树的重心,在一棵树中,树的重心可能不止一个。