poj 1741

地址:http://poj.org/problem?id=1741

题意:给一棵无根树,求任意两点距离小于等于k的点对数量。

mark:男人八题。。http://wenku.baidu.com/view/e087065f804d2b160b4ec0b5.html###

    上面网址是讲解思路的论文,具体实现没说。我来写写我的实现过程。

    首先是要找到重心,那么先递归求出每个点拆掉后能够得到的最大的点的个数,这个点的个数最小的肯定是重心。

    然后就按照论文讲解的方法求点对数量,先要求出每个点到重心的距离。

代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>

using namespace std;

const int N = 10010;
const int INF = 10000000;

struct node {

    int v,w;
    int sum,maxv;    //sum表示子孙节点个数,maxv表示拆了这个节点后剩余子树节点最多的个数 
    node *next;
}*head[N],tree[2*N],tp[N];
int n,k,ptr,ans,root,tot;
int w[N],vst[N];
int size[N],hash[N],dist[N];    //size表示最大分支的节点数 

void init()
{
    ans = ptr = 0;
    memset(vst, 0, sizeof(vst));
    memset(head, 0, sizeof(head));
}

void AddEdge(int x, int y, int z)
{
    tree[ptr].v = y;
    tree[ptr].w = z;
    tree[ptr].next = head[x],head[x] = &tree[ptr++];
}

void dfs(int s, int fa) 
{
    tp[s].sum = 1;
    tp[s].maxv = 0;
    node *p = head[s];
    while(p != NULL)
    {
        if(p->v != fa && !vst[p->v])
        {
            dfs(p->v, s);
            tp[s].sum += tp[p->v].sum;
            tp[s].maxv = max(tp[s].maxv, tp[p->v].sum);
        }
        p = p->next;
    }
    hash[tot] = s;
    size[tot++] = tp[s].maxv;
}

int getroot(int s)   //求重心。 
{
    tot = 0;
    dfs(s, 0);
    int tem = INF, maxr, cnt = tp[s].sum;
    for(int i = 0; i < tot; i++)
    {
        size[i] = max(size[i], cnt-size[i]);    //是求每个点拆掉后能得到的子树最多的点的个数! 
        if(size[i] < tem)
        {
            tem = size[i];
            maxr = hash[i];
        }
    }
    return maxr;
}

void getdis(int s, int fa, int dis)   //求每个点到根节点的距离 
{
    node *p = head[s];
    dist[tot++] = dis;
    while(p != NULL)
    {
        if(p->v != fa && !vst[p->v] && dis+p->w <= k)
            getdis(p->v, s, dis+p->w);
        p = p->next;
    }
}

void count1(int s)    //直接计算两点距离小于等于k的个数 
{
    sort(dist, dist+tot);
    int left = 0, right = tot-1;
    while(left < right)
    {
        if(dist[left] + dist[right] <= k)
            ans += right-left, left++;
        else right--;
    }
}

void count2(int s)   //由于count1多算了在一条分支上的点,需要排除 
{
    vst[s] = 1;
    node *p = head[s];
    while(p != NULL)
    {
        if(!vst[p->v])
        {
            tot = 0;
            getdis(p->v, s, p->w);
            sort(dist, dist+tot);
            int left = 0, right = tot-1;
            while(left < right)
            {
                if(dist[left]+dist[right] <= k)
                    ans -= right-left, left++;
                else right--;
            }
        }
        p = p->next;
    }
}

void solve(int s, int fa)
{
    root = getroot(s);
    tot = 0;
    getdis(root, 0, 0);
    count1(root);
    count2(root);
    node *p = head[root];
    while(p != NULL)
    {
        if(p->v != fa && !vst[p->v])
            solve(p->v, root);
        p = p->next;
    }
}


int main()
{
    int i,j;
    while(scanf("%d%d", &n, &k), n+k)
    {
        init();
        int aa,bb,cc;
        for(i = 1; i < n; i++)
        {
            scanf("%d%d%d", &aa, &bb, &cc);
            AddEdge(aa, bb, cc);
            AddEdge(bb, aa, cc);
        }
        solve(1, 0);
        printf("%d\n", ans);
    }
}

转载于:https://www.cnblogs.com/andre0506/archive/2012/10/12/2721113.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值