pku1985 Cow Marathon(树上的最远简单路)

//方法1,1141MS:

#include <stdio.h>
#include <string.h>
#include <vector>

using namespace std;

#define MAXN 40001

struct ENode
{
    int adv,dis;
};
vector<ENode> adjlist[MAXN];

int N,ans;
bool visited[MAXN];

int dfs(int i)
{//基于深度优先搜树(是一棵二叉树),不断比较左、右、相加谁大。。。
    visited[i]=true;
    int lv=-1,rv=-1;
    for(vector<ENode>::size_type j=0;j<adjlist[i].size();j++)
    {
        int v=adjlist[i][j].adv;
        if(!visited[v])
        {
            int tmp=dfs(v)+adjlist[i][j].dis;
            if(tmp>lv)
            {
                rv=lv;
                lv=tmp;
                /*
                此时这里包含两种情况之一。
                1.rv依然等于-1,表示这个tmp是左儿子返回值+adjlist[i][j].dis,而右儿子还未访问
                2.否则,是右儿子值返回值>左儿子返回值的情况,此时相当于交换了左右返回的值+adjlist[i][j].dis,
                保证了左返回值+adjlist[i][j].dis是较大的,好让dfs返回的是lv
                */
            }
            else if(tmp>rv)
            {//表示右儿子也访问完毕
                rv=tmp;
            }
        }
    }
    if(lv==-1) return 0;
    if(rv==-1)//表示只有左儿子,则要和ans比较谁大
    {
        if(lv>ans) ans=lv;
    }
    else
    {
        if(lv+rv>ans) ans=lv+rv;
    }
    return lv;
}

int main()
{
    int M,i,j,d;
    ENode e;
    char ch;
    while(scanf("%d %d",&N,&M)!=EOF)
    {
        getchar();
        memset(visited,false,sizeof(*visited)*(N+1));
        while(M--)
        {
            scanf("%d %d %d %c",&i,&j,&d,&ch);
            e.adv=j;
            e.dis=d;
            adjlist[i].push_back(e);
            e.adv=i;
            adjlist[j].push_back(e);
            getchar();
        }
        ans=-1;
        dfs(1);
        printf("%d\n",ans);
    }
    return 0;
}

 

//****************************************************************************

 

树的直径(Diameter)是指树上的最长简单路。
直径的求法:两遍BFS (or DFS)
任选一点u为起点,对树进行BFS遍历,找出离u最远的点v
以v为起点,再进行BFS遍历,找出离v最远的点w。则v到w的路径长度即为树的直径
*简单证明
于是原问题可以在O(E)时间内求出

关键在于证明第一次遍历的正确性,也就是对于任意点u,距离它最远的点v一定是最长路的一端。
如果u在最长路上,那么v一定是最长路的一端。可以用反证法:假设v不是最长路的一端,则存在另一点v’使得(u→v’)是最长路的一部分,于是len(u→v’) > len(u→v)。但这与条件“v是距u最远的点”矛盾。
如果u不在最长路上,则u到其距最远点v的路与最长路一定有一交点c,且(c→v)与最长路的后半段重合(why?),即v一定是最长路的一端

 

//方法2,两次BFS,1110MS
#include <stdio.h>
#include <string.h>
#include <vector>

using namespace std;

#define MAXN 40001

struct ENode
{
    int adv,dis;
};
vector<ENode> adjlist[MAXN];

int N,ans,dis[MAXN],queue[MAXN],front,rear;
bool visited[MAXN];

int bfs1(int u)
{
    front=rear=-1;
    queue[++rear]=u;
    int i,v;
    while(front<rear)
    {
        i=queue[++front];
        visited[i]=true;
        for(vector<ENode>::size_type j=0;j<adjlist[i].size();j++)
        {
            v=adjlist[i][j].adv;
            if(!visited[v]) queue[++rear]=v;
        }
    }
    return queue[rear];
}

void bfs2(int u)
{
    front=rear=-1;
    queue[++rear]=u;
    int i,v;
    while(front<rear)
    {
        i=queue[++front];
        visited[i]=true;
        for(vector<ENode>::size_type j=0;j<adjlist[i].size();j++)
        {
            v=adjlist[i][j].adv;
            if(!visited[v])
            {
                queue[++rear]=v;
                dis[v]=dis[i]+adjlist[i][j].dis;
                if(dis[v]>ans) ans=dis[v];
            }
        }
    }
}

int main()
{
    int M,i,j,d;
    ENode e;
    char ch;
    while(scanf("%d %d",&N,&M)!=EOF)
    {
        getchar();
        while(M--)
        {
            scanf("%d %d %d %c",&i,&j,&d,&ch);
            e.adv=j;
            e.dis=d;
            adjlist[i].push_back(e);
            e.adv=i;
            adjlist[j].push_back(e);
            getchar();
        }
        memset(visited,false,sizeof(*visited)*(N+1));
        int v=bfs1(1);
        memset(visited,false,sizeof(*visited)*(N+1));
        memset(dis,0,sizeof(dis));
        ans=-1;
        bfs2(v);
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2010-08-20 15:25  菜到不得鸟  阅读(341)  评论(0)    收藏  举报