1. 程式人生 > 實用技巧 >POJ 1655 Balancing Act ( 樹的重心板子題,鏈式前向星建圖)

POJ 1655 Balancing Act ( 樹的重心板子題,鏈式前向星建圖)

題意:

給你一個由n個節點n-1條邊構成的一棵樹,你需要輸出樹的重心是那個節點,以及重心刪除後得到的最大子樹的節點個數size,如果size相同就選取編號最小的

題解:

樹的重心定義:
找到一個點,其所有的子樹中最大的子樹節點數最少,那麼這個點就是這棵樹的重心,刪去重心後,
生成的多棵樹儘可能平衡。

洛谷中P5666樹的重心對樹的重心還有這樣一種描述:

一個大小為n的樹由nn個結點與n−1條無向邊構成,且滿足任意兩個結點間有且僅有一條簡單路徑。在樹中刪去一個結點及與它關聯的邊,樹將分裂為若干個子樹;而在樹中刪去一條邊(保留關聯結點,下同),樹將分裂為恰好兩個子樹。

對於一個大小為n的樹與任意一個樹中結點c,稱c是該樹的重心當且僅當在樹中刪去c及與它關聯的邊後,分裂出的所有子樹的大小均不超過 floor(n/2)(其中floor⌊x⌋是向下取整函式)。對於包含至少一個結點的樹,它的重心只可能有 1 或 2 個。

樹的重心的性質:
1.樹中所有點到某個點的距離和中,到重心的距離和是最小的;如果有兩個重心,那麼他們的距離和一樣。
2.把兩個樹通過一條邊相連得到一個新的樹,那麼新的樹的重心在連線原來兩個樹的重心的路徑上。
3.把一個樹新增或刪除一個葉子,那麼它的重心最多隻移動一條邊的距離。

迴歸原題:

這道題我們可以隨便找一個節點當作樹根,然後dfs處理出來每一個節點的子節點的數量sonnum和每一個節點的子樹中最大那顆子樹的大小sonmax

樹的定義中說過:“找到一個點,其所有的子樹中最大的子樹節點數最少,那麼這個點就是這棵樹的重心

那麼對於一個節點x,它所有子樹中最大子樹節點數就是:max(sonmax[i],n-sonnum[i])

sonmax[i]:表示的是x節點的子樹中最大那顆子樹的大小

n-sonnum[i]:表示的是不是x的子節點的剩下所有點構成的那顆樹的大小

程式碼:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define mem(a) memset(a,0,sizeof(a))
#define mem_(a) memset(a,-1,sizeof(a))
const
int maxn=2e4+10; const int INF=0x3f3f3f3f; //鏈式前向星 struct node { int to,w,next; }e[maxn*2]; //這個是有多少邊陣列就開多少 int head[maxn],cnt,sonnum[maxn],sonmax[maxn]; void add_edge(int x,int y,int z) { e[cnt].to=y; e[cnt].w=z; e[cnt].next=head[x]; head[x]=cnt++; } void dfs(int now,int pre) { sonnum[now]=1; sonmax[now]=0; for(int i=head[now];~i;i=e[i].next) { int to=e[i].to; if(to==pre) continue; dfs(to,now); sonnum[now]+=sonnum[to]; sonmax[now]=max(sonmax[now],sonnum[to]); } } int main() { int t; scanf("%d",&t); while(t--) { int n; scanf("%d",&n); cnt=1; mem_(head); for(int i=1;i<n;++i) { int x,y; scanf("%d%d",&x,&y); add_edge(x,y,0); add_edge(y,x,0); } dfs(1,0); int minn=INF,pos; for(int i=1;i<=n;++i) { //根據樹的重心的定義,我們發現判斷一個點是不是重心 只要把這個點去掉 //看它剩下的子樹結點的個數的最大值是不是最小就ok了 //子樹有兩種:一個是往下的即sonMax[i],另一個是往上的 即n - sonNum[i] //printf("%d %d***********\n",sonmax[i],n-sonnum[i]); if(minn>max(sonmax[i],n-sonnum[i])) { minn=max(sonmax[i],n-sonnum[i]); pos=i; } } printf("%d %d\n",pos,minn); } return 0; }