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求出離其最遠的點,在用同樣的方法求出離這個葉子節點最遠的點,此時兩點間的距離就是樹的直徑。

  題意:有 nn 個農田和 mm 條路(無環),找到一條路徑(任意兩點)使得這個距離最長。
  思路一:用兩次 dfsdfs 來求樹的直徑從樹上任意一點 uu 找到一個距離它最遠的點 vv,然後以 vv 爲源點,找到距離 vv 最遠的點 wwvwv - w 即爲樹的直徑。
證明(反證法): 假設 vwv - w 爲樹的直徑。那麼 uudfsdfs 能訪問到的最遠的點一定是 vv 或者 ww,如果不是我們假設它爲 maxmax
1.1 . uuvwv - w 上,如圖:
        在這裏插入圖片描述
{dis(u,mmax)>dis(u,v)dis(u,mmax)>dis(u,w)\begin{cases} dis(u,mmax)>dis(u,v)\\ dis(u,mmax)>dis(u,w) \end{cases}  {dis(u,mmax)+dis(u,v)=dis(v,mmax)dis(u,v)+dis(u,w)=dis(v,w)\begin{cases} dis(u,mmax)+dis(u,v)=dis(v,mmax) \\ dis(u,v)+dis(u,w)=dis(v,w) \end{cases}

         ==>>dis(v,mmax)dis(v,mmax)>>dis(v,w)dis(v,w)

上述結果與剛開始 vwv - w是樹的直徑矛盾
2.2.uu 不在 vwv - w 上,如圖:
在這裏插入圖片描述{dis(u,mmax)>dis(u,t)+dis(t,v)dis(u,mmax)>dis(u,t)+dis(t,w)\begin{cases} dis(u,mmax)>dis(u,t) + dis(t,v)\\ dis(u,mmax)>dis(u,t) +dis(t,w) \end{cases}  

{dis(u,mmax)+dis(u,t)+dis(t,v)=dis(v,mmax)dis(v,t)+dis(t,w)=dis(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}

==>>dis(v,mmax)dis(v,mmax)>>dis(v,w)dis(v,w)

上述結果與剛開始 vwv - w是樹的直徑矛盾。
綜上所述可以證明:vwv - 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
*/

思路二:
dp[i][0]dp[i][0]:結點 ii 的子樹到 ii 的最長距離
dp[i][1]dp[i][1]:結點 ii 的子樹到 ii 的次長距離
dp[i][2]dp[i][2]:從結點 ii 往上走的最長距離
每個結點的最長和次長距離我們用 dpdp 的方式自下而上的去更新(保證次長和最長分別分佈在左右子樹上),算出 dp[i][0]dp[i][0]dp[i][1]dp[i][1] 後,如果要計算 uu 的上面的最長距離,我們可以發現:
如果 uu 不在其父親的最長鏈上,那麼距離他最長的距離就是
max()+max(他父親往上走的最長距離,他父親的子樹的最長距離) + 他們之間的距離
如果 uu 在其父親的最長鏈上,那麼距離他最長的距離就是
max()+max(他父親往上走的最長距離,他父親的子樹的次長距離) + 他們之間的距離
上述語言符號化:
if(dp[v][0]+w[u][v]==dp[u][0])if(dp[v][0]+w[u][v]==dp[u][0]) 兒子在父親的最長鏈上
dp[v][2]=max(dp[u][2],dp[u][1])+w[u][v]dp[v][2]=max(dp[u][2],dp[u][1]) + w[u][v]
elseelsedp[v][2]=max(dp[u][2],dp[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
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章