树的分治 poj 1741

22 篇文章 0 订阅
15 篇文章 0 订阅

求树的重心结点:用标号定义树的重心,对于一个结点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
*/ 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值