树的直径

  noip 2018 day1 t3听学长说是二分+树形dp+二分。但是暴力分给的还是挺足的55分。

20分是树的直径,无奈不会求,n^n暴力dfs只拿了10分,血亏一波。

1.树形dp求树的直径

设当前节点为x,d[x]表示以x为节点所能到达的最长链。显然对于穿过x的树的直径=d[x]+d[tn]+e(x,tn);

这样直接一遍dp即可。

void dp(long long x)
{    
    vis[x]=1;
    for(long long i=lin[x];i;i=nex[i])
    {
        long long tn=ver[i];
        if(vis[tn]==1)continue;
        dp(tn);
        cnt=max(cnt,d[x]+d[tn]+e[i]);
        d[x]=max(d[x],d[tn]+e[i]);
    }
    return;
}
//cnt 即为答案

2.通过两遍dfs或bfs来求树的直径。

先任取一个点求出距离此点最远的距离的点,然后从这个点在跑出距离当前点最远的点即可。

我觉得不是很显然,当然也不会证,画出不同种的情况来显然一下吧。

dfs:

    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn]==1)continue;
        vis[tn]=1;
        d[tn]=max(d[tn],d[x]+e[i]);
        if(d[tn]>cnt)cnt=d[tn],u=tn;
        dfs(tn);
    }
        cnt=0;vis[1]=1;dfs(1);
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    cnt=0;vis[u]=1;dfs(u);
    put(cnt);

bfs也是相同的道理。

h=0,t=0;cnt=0;
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    q[++t]=1;vis[1]=1;
    while(h++<t)
    {
        long long te=q[h];
        for(long long i=lin[te];i;i=nex[i])
        {
            long long tn=ver[i];
            if(vis[tn]==1)continue;
            d[tn]=max(d[tn],d[te]+e[i]);//取max更加严谨,尽管更慢
            if(d[tn]>cnt)cnt=d[tn],u=tn;
            q[++t]=tn;vis[tn]=1;
        }
    }
    //cout<<u<<endl;
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    h=0,t=0;cnt=0;
    q[++t]=u;vis[u]=1;
    //cout<<u<<endl;
    while(h++<t)
    {
        long long te=q[h];
        for(long long i=lin[te];i;i=nex[i])
        {
            long long tn=ver[i];
            if(vis[tn]==1)continue;
            d[tn]=max(d[tn],d[te]+e[i]);//取max更加严谨,尽管更慢
            if(d[tn]>cnt)cnt=d[tn];
            q[++t]=tn;vis[tn]=1;
        }
    }
    put(cnt);return;

dfs,bfs便利两次可能常数较大,但是可以求出一些关键点的信息,如直径的两端等等。

很简单的东西。

原文地址:https://www.cnblogs.com/chdy/p/10131065.html