施工方案第二季 CODEVS - 1700

本文介绍了一种求解树的直径及寻找树的重心的算法。通过两次深度优先搜索确定树的直径,并在此基础上进一步查找树的重心,即到所有节点距离之和最小的节点。文章提供了一个完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击打开链接

对于一棵树 我们以任一节点为根节点 找到与其相距最远的一个点p1 再以p1为根节点 找到与其相距最远的一个点p2

此时p1到p2的路径就是树的直径 而树的重心(到其他节点的最远距离最小)就在这个直径上

至于为什么..我也不清楚

 

#include <bits/stdc++.h>
using namespace std;
#define ll long long

struct node1
{
    int u;
    int v;
    ll w;
};

struct node2
{
    int v;
    ll w;
    int next;
};

node1 edge1[200010];
node2 edge2[100010];
int f[100010],first[100010],book[100010],core[100010];
ll ans1,ans2,maxx,dia;
int n,m,num,p1,p2,tar;

int cmp(node1 n1,node1 n2);
void addedge(int u,int v,ll w);
int unite(int u,int v);
int getf(int p);
void dfs1(int cur,ll dis);
int dfs2(int cur);
void dfs3(int cur,ll dis);

int main()
{
    int i,j,cnt;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%lld",&edge1[i].u,&edge1[i].v,&edge1[i].w);
        }
        sort(edge1+1,edge1+m+1,cmp);
        for(i=1;i<=n;i++)
        {
            f[i]=i;
        }
        memset(first,-1,sizeof(first));
        num=0,ans1=0,cnt=0;
        for(i=1;i<=m;i++)
        {
            if(unite(edge1[i].u,edge1[i].v))
            {
                addedge(edge1[i].u,edge1[i].v,edge1[i].w);
                addedge(edge1[i].v,edge1[i].u,edge1[i].w);
                ans1+=edge1[i].w;
                cnt++;
            }
            if(cnt==n-1) break;
        }

        memset(book,0,sizeof(book));
        book[1]=1;
        maxx=0;
        dfs1(1,0);
        p1=p2;
        memset(book,0,sizeof(book));
        book[p2]=1;
        maxx=0;
        dfs1(p2,0);

        memset(book,0,sizeof(book));
        memset(core,0,sizeof(core));
        book[p1]=1;
        core[p1]=1;
        dia=0;
        dfs2(p1);

        dfs3(p1,0);

        if(ans2<dia/2) ans2=dia-ans2;
        printf("%lld\n%lld\n",ans1,ans2);
    }
    return 0;
}

int cmp(node1 n1,node1 n2)
{
    return n1.w<n2.w;
}

void addedge(int u,int v,ll w)
{
    edge2[num].v=v;
    edge2[num].w=w;
    edge2[num].next=first[u];
    first[u]=num++;
    return;
}

int unite(int u,int v)
{
    int fu,fv;
    fu=getf(u);
    fv=getf(v);
    if(fu!=fv)
    {
        f[fv]=fu;
        return 1;
    }
    else
    {
        return 0;
    }
}

int getf(int p)
{
    if(f[p]==p) return f[p];
    else
    {
        f[p]=getf(f[p]);
        return f[p];
    }
}

void dfs1(int cur,ll dis)
{
    ll w;
    int i,v;
    if(dis>maxx)
    {
        maxx=dis;
        p2=cur;
    }
    for(i=first[cur];i!=-1;i=edge2[i].next)
    {
        v=edge2[i].v,w=edge2[i].w;
        if(book[v]==0)
        {
            book[v]=1;
            dfs1(v,dis+w);
        }
    }
    return;
}

int dfs2(int cur)
{
    ll w;
    int i,v,flag;
    if(cur==p2) return 1;
    flag=0;
    for(i=first[cur];i!=-1;i=edge2[i].next)
    {
        v=edge2[i].v,w=edge2[i].w;
        if(book[v]==0)
        {
            book[v]=1;
            flag=dfs2(v);
        }
        if(flag==1)
        {
            core[v]=1;
            dia+=w;
            return 1;
        }
    }
    return 0;
}

void dfs3(int cur,ll dis)
{
    ll w;
    int i,v;
    for(i=first[cur];i!=-1;i=edge2[i].next)
    {
        v=edge2[i].v,w=edge2[i].w;
        if(core[v]==1)
        {
            core[v]=0;
            if((fabs(dia/2-dis))<(fabs(dia/2-dis-w)))
            {
                ans2=dis;
                return;
            }
            dfs3(v,dis+w);
        }
    }
    return;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值