POJ 1985 Cow Marathon(树的直径 - bfs / 树形dp)

链接:http://poj.org/problem?id=1985
来源:POJ

  • Description
    After hearing about the epidemic of obesity in the USA, Farmer John wants his cows to get more exercise, so he has committed to create a bovine marathon for his cows to run. The marathon route will include a pair of farms and a path comprised of a sequence of roads between them. Since FJ wants the cows to get as much exercise as possible he wants to find the two farms on his map that are the farthest apart from each other (distance being measured in terms of total length of road on the path between the two farms). Help him determine the distances between this farthest pair of farms.
  • Input
    Lines 1…: Same input format as “Navigation Nightmare”.
  • Output
    Line 1: An integer giving the distance between the farthest pair of farms.
  • Sample Input
    7 6
    1 6 13 E
    6 3 9 E
    3 5 7 S
    4 1 3 N
    2 4 20 W
    4 7 2 S
  • Sample Output
    52
  • Hint
    The longest marathon runs from farm 2 via roads 4, 1, 6 and 3 to farm 5 and is of length 20+3+13+9+7=52.
  • 树的直径
      树上最远两点的距离。距某个点最远的叶子节点一定是树的某一条直径的端点。这样就可以首先任取一个点,bfs求出离其最远的点,在用同样的方法求出离这个叶子节点最远的点,此时两点间的距离就是树的直径。

  题意:有 n n 个农田和 m m 条路(无环),找到一条路径(任意两点)使得这个距离最长。
  思路一:用两次 d f s dfs 来求树的直径 从树上任意一点 u u 找到一个距离它最远的点 v v ,然后以 v v 为源点,找到距离 v v 最远的点 w w v w v - w 即为树的直径。
证明(反证法): 假设 v w v - w 为树的直径。那么 u u d f s dfs 能访问到的最远的点一定是 v v 或者 w w ,如果不是我们假设它为 m a x max
1. 1 . u u v w v - w 上,如图:
        在这里插入图片描述
{ d i s ( u , m m a x ) > d i s ( u , v ) d i s ( u , m m a x ) > d i s ( u , w ) \begin{cases} dis(u,mmax)>dis(u,v)\\ dis(u,mmax)>dis(u,w) \end{cases}    { d i s ( u , m m a x ) + d i s ( u , v ) = d i s ( v , m m a x ) d i s ( u , v ) + d i s ( u , w ) = d i s ( v , w ) \begin{cases} dis(u,mmax)+dis(u,v)=dis(v,mmax) \\ dis(u,v)+dis(u,w)=dis(v,w) \end{cases}

          = = > > d i s ( v , m m a x ) dis(v,mmax) > > d i s ( v , w ) dis(v,w)

上述结果与刚开始 v w v - w 是树的直径矛盾
2. 2. u u 不在 v w v - w 上,如图:
在这里插入图片描述 { d i s ( u , m m a x ) > d i s ( u , t ) + d i s ( t , v ) d i s ( u , m m a x ) > d i s ( u , t ) + d i s ( t , w ) \begin{cases} dis(u,mmax)>dis(u,t) + dis(t,v)\\ dis(u,mmax)>dis(u,t) +dis(t,w) \end{cases}   

{ d i s ( u , m m a x ) + d i s ( u , t ) + d i s ( t , v ) = d i s ( v , m m a x ) d i s ( v , t ) + d i s ( t , w ) = d i s ( v , w ) \begin{cases} dis(u,mmax)+dis(u,t)+dis(t,v)=dis(v,mmax) \\ dis(v,t)+dis(t,w)=dis(v,w) \end{cases}

= = > > d i s ( v , m m a x ) dis(v,mmax) > > d i s ( v , w ) dis(v,w)

上述结果与刚开始 v w v - w 是树的直径矛盾。
综上所述可以证明: v w v - w 是树的直径。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;

typedef long long ll;
const int Max_n=1e6+10;
int head[Max_n],cnt,mmaxdot,dis[Max_n],ans;
bool vis[Max_n];
int n,m;

struct Edge{
    int u,v,w,net;
}edge[Max_n<<1];

void addedge(int u,int v,int w){
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].net=head[u];
    head[u]=cnt++;
}

void bfs(int s){
    memset(dis,0,sizeof(dis));
    memset(vis,false,sizeof(vis));
    queue<int>q; q.push(s); ans=0;
    vis[s]=true;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i!=-1;i=edge[i].net){
            int v=edge[i].v;
            if(!vis[v]){
                if(dis[v]<dis[u]+edge[i].w){
                    dis[v]=dis[u]+edge[i].w;
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(ans<dis[i]){
            ans=dis[i]; mmaxdot=i;
        }
    }
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) head[i]=-1;
    for(int i=1;i<=m;i++){
        int u,v,w; char str;
        scanf("%d%d%d %c",&u,&v,&w,&str);
        addedge(u,v,w); addedge(v,u,w);
    }
    bfs(1);
    //for(int i=1;i<=n;i++) cout<<dis[i]<<" ";
    //cout<<ans<<endl;
    //cout<<mmaxdot<<endl;
    bfs(mmaxdot);
    //for(int i=1;i<=n;i++) cout<<dis[i]<<" ";
    printf("%d\n",ans);
    return 0;
}


/**
* Copyright(c)
* All rights reserved.
* Author : Max_n
* Date : 2019-10-05-14.15.20
* Description : 树的直径 - 两次 bfs
*/

思路二:
d p [ i ] [ 0 ] dp[i][0]: 结点 i i 的子树到 i i 的最长距离
d p [ i ] [ 1 ] dp[i][1]: 结点 i i 的子树到 i i 的次长距离
d p [ i ] [ 2 ] dp[i][2]: 从结点 i i 往上走的最长距离
每个结点的最长和次长距离我们用 d p dp 的方式自下而上的去更新(保证次长和最长分别分布在左右子树上),算出 d p [ i ] [ 0 ] dp[i][0] d p [ i ] [ 1 ] dp[i][1] 后,如果要计算 u u 的上面的最长距离,我们可以发现:
如果 u u 不在其父亲的最长链上,那么距离他最长的距离就是
m a x ( ) + max(他父亲往上走的最长距离,他父亲的子树的最长距离) + 他们之间的距离
如果 u u 在其父亲的最长链上,那么距离他最长的距离就是
m a x ( ) + max(他父亲往上走的最长距离,他父亲的子树的次长距离) + 他们之间的距离
上述语言符号化:
i f ( d p [ v ] [ 0 ] + w [ u ] [ v ] = = d p [ u ] [ 0 ] ) if(dp[v][0]+w[u][v]==dp[u][0]) 儿子在父亲的最长链上
d p [ v ] [ 2 ] = m a x ( d p [ u ] [ 2 ] , d p [ u ] [ 1 ] ) + w [ u ] [ v ] dp[v][2]=max(dp[u][2],dp[u][1]) + w[u][v]
e l s e else d p [ v ] [ 2 ] = m a x ( d p [ u ] [ 2 ] , d p [ u ] [ 0 ] ) + w [ u ] [ v ] dp[v][2]=max(dp[u][2],dp[u][0])+w[u][v] 儿子不在父亲的最长链上
这样我们求出每个结点往下走最远的距离和他往上走的最远距离

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;

typedef long long ll;
const int Max_n=1e6+10;
int cnt,head[Max_n],dp[Max_n][3];
bool vis[Max_n];

struct Edge{
    int u,v,w,next;
}edge[Max_n<<1];

void addedge(int u,int v,int w){
    edge[cnt].u=u; edge[cnt].v=v;
    edge[cnt].w=w; edge[cnt].next=head[u];
    head[u]=cnt++;
}

void dfs1(int u){
    vis[u]=true;
    int one=0,two=0;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!vis[v]){
            dfs1(v);
            int cost=dp[v][0]+edge[i].w;
            if(cost>=one){ two=one; one=cost; }
            if(cost<one&&cost>two) two=cost;
        }
    }
    dp[u][0]=one;
    dp[u][1]=two;
}

void dfs2(int u){
    vis[u]=true;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!vis[v]){
            //v 在 u 的最长链上
            if(dp[v][0]+edge[i].w==dp[u][0]) dp[v][2]=max(dp[u][2],dp[u][1])+edge[i].w;
            else dp[v][2]=max(dp[u][2],dp[u][0])+edge[i].w;
            dfs2(v);
        }
    }
}

int main(){
    int n,m; scanf("%d%d",&n,&m);
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++) head[i]=-1;
    for(int i=1;i<=m;i++){
        int u,v,w; char str;
        scanf("%d%d%d %c",&u,&v,&w,&str);
        addedge(u,v,w); addedge(v,u,w);
    }
    memset(vis,false,sizeof(vis)); dfs1(1);
    //for(int i=1;i<=n;i++) cout<<dp[i][0]<<" "; cout<<endl;
    memset(vis,false,sizeof(vis)); dfs2(1);
    //for(int i=1;i<=n;i++) cout<<dp[i][2]<<" "; cout<<endl;
    int ans=0;
    for(int i=1;i<=n;i++) ans=max(ans,dp[i][2]+dp[i][0]);
    printf("%d\n",ans);
    //cout<<ans<<endl;
    return 0;
}


/**
* Copyright(c)
* All rights reserved.
* Author : Max_n
* Date : 2019-10-06-19.26.42
* Description : 树的直径 - 树形dp
*/
发布了166 篇原创文章 · 获赞 68 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_42217376/article/details/102148990