求树的重心结点:用标号定义树的重心,对于一个结点K,如果把K从树中删除(连同与它相连的边一起),剩下的被分成了很多块,每一块显然又是一棵树(即剩下的部份构成了一个森林)。则给结点K所标的号就是森林中结点个数最多的树所拥有的结点数。如果结点K的标号不大于其它他任何一个结点的标号,则结点K被称为是树的重心。
从树种找到重心,在以每个重心分为的森林找每棵树的重心,每找到一个重心,就以他为中转点算出该树中其他节点到此重心的路径长度,将每个节点编号排序,若最小的 i 与最大的路径 j 长度和小于m,则j - i 代表可以匹配的节点对数。为避免出现节点对为同一子树中,则要减去以当前树的子树为根节点的节点对。最后将每个点的节点对数相加,即为结果。
附上参考代码+个人注释(不到位,请指点!~~)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include<iostream> using namespace std; #define MAX_N 11000 struct tnode { int t, w, n; }; tnode e[MAX_N * 2]; int fi[MAX_N], d[MAX_N], size[MAX_N]; int f[MAX_N]; bool used[MAX_N]; int tot, rs, root, sx; int n, m, es; void inserte(int x, int y, int z) { e[++es].t = y, e[es].w = z; e[es].n = fi[x]; // cout<<"es= "<<es<<" e[es].t= "<<y<<" e[es].w= "<<z<<" e[es].n= "<<fi[x]; fi[x] = es; // cout<<" fi[ "<<x<<" ]= "<<fi[x]<<endl; } void init(void) { memset (fi, -1, sizeof (fi)); memset (used, false, sizeof (used)); es = -1; for (int i = 1, x, y, z; i < n; i++) { scanf ("%d%d%d", &x, &y, &z); inserte (x, y, z); inserte (y, x, z); } } void get_root(int x, int p) { int big = -1; size[x] = 1; for (int i = fi[x], t; i != -1; i = e[i].n) //i表示与x相连的边 { t = e[i].t; //该边的另一个点 if (used[t] || t == p) continue; //若曾以该节点为根节点搜到过,或已经没有与它相连的边,或已经搜回到此节点 get_root (t, x); /////cout<<"size["<<t<<"]= "<<size[t]<<endl; size[x] += size[t]; /////cout<<" size["<<x<<"]= "<<size[x]<<endl; if (size[t] > big) big = size[t]; //size表示当前划分方法下,划分后的节点数 } if (sx - size[x] > big) big = sx - size[x]; //sx表示当前这棵树的节点总数 ///// cout<<"********sx= "<<sx<<" rs= "<<rs<<" big= "<<big<<endl; //rs表示上一次找到的节点若为该树的重心时,删除该节点后所成最大树的节点数。 //只有big<rs才说明现在找到的节点分成的森林中最大树的节点数比之前找到的要少。 //于是更换”重心" if (big < rs) {rs = big, root = x; }///// cout<<"@@@rs= "<<rs<<" root= "<<x<<endl;} ///// else cout<<"!!!!!!!!!!!!!!!!!rs= "<<rs<<" root= "<<root<<endl; } int find_root(int x) { rs = sx = size[x]; get_root (x, -1); return root; } void get_dis(int x, int dis, int p) //当前根节点x分别到所有其他节点的路经长。 { d[++tot] = dis; cout<<"d["<<tot<<"]= "<<dis<<endl; for (int i = fi[x], t; i != -1; i = e[i].n) { t = e[i].t; cout<<fi[x]<<" i ="<<i<<" ***ttt= "<<t<<endl; if (used[t] || t == p) continue; get_dis (t, dis + e[i].w, x); cout<<" t= "<<t<<endl; } } int count(int x, int y) { cout<<"XXXXXXXXXXXXX= "<<x<<" YYYYY= "<<y<<endl; int s = 0; tot = 0, get_dis (x, y, -1); // cout<<"tot= "<<tot<<endl; sort (d + 1, d + tot + 1); //将当前x到各个节点,按路径长排序 // for(int i=1;i<=tot;i++) cout<<" *** d["<<i<<"]= "<<d[i]<<endl; for (int i = 1, j = tot; i <= j; i++) //将最短的与最长的匹配,若可匹配则该短路径的可以和其他中间路径的匹配 { while (d[i] + d[j] > m && i <= j) {j--; } // cout<<i<<" "<<j<<endl; if (i < j) s += j - i; //计算可以匹配的数量 // cout<<"ssssss= "<<s<<endl; } return s; } void dfs(int x) { x = find_root (x); cout<<"zhongxin= "<<x<<endl; f[x] = count (x, 0); cout<<"f["<<x<<"]= "<<f[x]<<endl; used[x] = true; for (int i = fi[x], t; i != -1; i = e[i].n) { t = e[i].t; if (used[t]) continue; f[x] -= count(t, e[i].w); //为了计算以x为根节点的树上可以匹配的数量(除去子树上可匹配的,即路径只能是从下往上倒根节点在向下。) // cout<<"c= "<<c<<" f"<<x<<"]= "<<f[x]<<" t= "<<t<<endl; dfs(t); } } int main(void) { // freopen ("1741.in", "r", stdin); // freopen ("1741.out", "w", stdout); while (scanf ("%d%d", &n, &m) != EOF && n) { init(); size[1] = n, dfs (1); int ans = 0; for (int i = 1; i <= n; i++) ans += f[i]; printf ("%d\n", ans); } return 0; } /* 18 10 1 2 5 1 3 5 1 4 5 2 8 2 2 9 5 3 5 5 3 7 5 3 6 5 4 10 5 4 11 5 7 12 5 7 13 5 8 15 5 8 16 5 12 17 5 12 18 5 15 14 5 */