1. 程式人生 > 實用技巧 >樹的重心入門

樹的重心入門

  • 樹的重心對於一棵\(n\)個結點的無根樹,找到一個點,使得把樹變成以該點為根的有根樹樹時,最大子樹的結點數最小。刪去重心後,生成的多棵樹儘可能平衡。
  • 性質:
    • 性質 1:樹中所有點到某個點的距離和中,到重心的距離和是最小的,如果有兩個重心,他們的距離和一樣。
    • 性質 2:把兩棵樹通過某一點相連得到一顆新的樹,新的樹的重心必然在連線原來兩棵樹重心的路徑上。
    • 性質 3:一棵樹新增或者刪除一個節點,樹的重心最多隻移動一條邊的位置。
    • 性質 4:一棵樹最多有兩個重心,且相鄰。
  • 模板詳解核心\(dfs\)程式碼
  •  1 ll minl=inf,ans;
     2 /*
     3 sz陣列表示以當前節點為根,向下的子樹的節點數目
    
    4 dp陣列表示以當前節點為根,子樹中最大節點數目是多少 5 */ 6 7 void dfs(int u,int fa){ 8 sz[u]=1; 9 for(int i=head[u];~i;i=edge[i].nxt){ 10 int v=edge[i].to; 11 if( v!=fa ){//無向圖,不加這個會成環 12 dfs(v,u); 13 sz[u]+=sz[v];//回溯加一下 14 dp[u]=max(dp[u],sz[v]);//每次遍歷完一棵子樹都比較 15
    } 16 } 17 18 dp[u]=max(dp[u],n-sz[u]);//不要忽略向上的節點個數,比較完之後便是當前節點的最大子樹節點 19 if( minl>dp[u]){ 20 minl=dp[u]; 21 ans=u; 22 } 23 }

  • 入門例題:here
    • 題意:求刪除這個點後最大子樹的節點數,以及這個點的編號,如果有多個點滿足,求編號最小的點。
    • AC_Code:
    •  1 #include <iostream>
       2 #include <string.h>
       3
      #include <stdio.h> 4 5 using namespace std; 6 const int maxn = 20005; 7 const int inf = 0x3f3f3f3f; 8 9 int head[maxn]; 10 int sz[maxn]; 11 int cnt,n; 12 int ans,siz; 13 14 struct Edge{ 15 int to; 16 int nxt; 17 }; 18 19 Edge edge[2*maxn]; 20 21 void Init(){ 22 cnt=0; 23 siz=inf; 24 memset(head,-1,sizeof(head)); 25 } 26 27 void add(int u,int v){ 28 edge[cnt].to = v; 29 edge[cnt].nxt = head[u]; 30 head[u] = cnt++; 31 } 32 33 void dfs(int u,int fa) 34 { 35 sz[u]=1; 36 int tmp=0; 37 for(int i=head[u];~i;i=edge[i].nxt){ 38 int v=edge[i].to; 39 if(v!=fa){ 40 dfs(v,u); 41 sz[u]+=sz[v]; 42 tmp=max(tmp,sz[v]); 43 } 44 } 45 tmp=max(tmp,n-sz[u]); 46 if(tmp<siz || (tmp==siz && u<ans)){ 47 ans=u; 48 siz=tmp; 49 } 50 } 51 52 int main() 53 { 54 int t; 55 scanf("%d",&t); 56 while(t--){ 57 Init(); 58 scanf("%d",&n); 59 for(int i=1;i<=n-1;i++){ 60 int u,v; 61 scanf("%d%d",&u,&v); 62 add(u,v);add(v,u); 63 } 64 dfs(1,0); 65 printf("%d %d\n",ans,siz); 66 } 67 return 0; 68 }