zoukankan      html  css  js  c++  java
  • 求树的直径+并查集(bfs,dfs都可以)hdu4514

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4514

    这题主要是叫我们求出树的直径,在求树的直径之前要先判断一下有没有环

    树的直径指的就是一棵树上面距离最远的两点的距离,有时也可以指最远的两点之间的路径。

    至于树的直径怎么求,我们首先要知道一个结论,树上面随便取一点,离这一点最远的那个点一定是树的直径上面的两点中的一点

    证明的博客:https://www.cnblogs.com/wuyiqi/archive/2012/04/08/2437424.html

    知道了这个结论,我们就可以用两次dfs或者两次bfs来求出树的直径,第一次bfs我们随便拿树上的一个点进行bfs,去找到离他最远的一点,这样我们就找到了树的直径两端上面的一点,然后第二次bfs就以这一点为开始去找到离这一点距离最大的点,得到的这这个点就树的直径两端的另外一个点,这两点之间的距离就是树的直径。

    思路:首先我们要判断是否有环,这里用的是并查集,一旦给出的树边上的两点已经在一个集合里面了,说明之前这两点之间就有一条互通的路径,所以加上增加的这条树边就构成了一个环。然后注意这题给出的数据不一定只是一颗树,可能是森林,所以可能有多个树的直径,我们取最大值。

    我的代码写的不是很好,绝对不是最优的,仅供参考。

    bfs写的代码:

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<map>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<set>
    #include<cstdio>
    #include<string>
    #include<deque> 
    using namespace std;
    typedef long long LL;
    #define eps 1e-8
    #define INF 0x3f3f3f3f
    #define maxn 1000005
    int head[maxn],pre[maxn],dis[maxn],vis[maxn];
    int n,m,k,t,cnt,flag,max1,point;
    //max1记录每次bfs的最大距离,point记录离树根最远的点 
    struct node{
        int v,w,next;
    }edge[maxn*20];
    void init(){
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)
        pre[i]=i;
        cnt=flag=0;
    }
    int find(int a){
        if(pre[a]==a)
        return a;
        return pre[a]=find(pre[a]);
    }
    void add(int u,int v,int w){
        edge[++cnt].v=v;
        edge[cnt].w=w;
        edge[cnt].next=head[u];
        head[u]=cnt;
    }
    void bfs(int u){//bfs找以点u为子树的所有点里面离点u最远的点和这个最远的距离 
        queue<int>q;
        dis[u]=0;
        q.push(u);
        while(!q.empty()){
            u=q.front();
            q.pop();
            vis[u]=true;
            for(int i=head[u];i!=-1;i=edge[i].next){
                int v=edge[i].v;
                int w=edge[i].w;
                if(!vis[v]){
                    dis[v]=dis[u]+w;
                    q.push(v);
                    if(max1<dis[v]){//更新最远的点和最大距离 
                        max1=dis[v];
                        point=v;
                    }
                }
            }
        }
        
    }
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF){
            init();
            int u,v,w;
            for(int i=1;i<=m;i++){
                scanf("%d%d%d",&u,&v,&w);
                if(flag)
                continue;
                int x=find(u);
                int y=find(v);
                if(x==y)//并查集判断是否有环 
                flag=1;
                else{
                    pre[x]=y;
                    add(u,v,w);
                    add(v,u,w);
                }
            }
            if(flag){
                printf("YES
    ");
                continue;
            }
            memset(vis,0,sizeof(vis));
            memset(dis,0,sizeof(dis));
            stack<int>ss;//因为可能给的数据是森林,不一定是只有一棵树,所以我把每颗树里面
            //离树根最远的点存进栈里面 
            for(int i=1;i<=n;i++){
                if(vis[i])
                continue;
                max1=0;
                bfs(i);
                ss.push(point);
            }
            int ans=0;
            memset(vis,0,sizeof(vis));
            memset(dis,0,sizeof(vis)); 
            while(!ss.empty()){//再用栈里面的点来进行第二次bfs找到树的直径的另外一个点和树的直径 
                point=ss.top();
                ss.pop();
                max1=0;
                bfs(point);
                ans=max(max1,ans);
            }
            printf("%d
    ",ans);
        }
        return 0;
    }

    dfs写的代码:

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<map>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<set>
    #include<cstdio>
    #include<string>
    #include<deque> 
    using namespace std;
    typedef long long LL;
    #define eps 1e-8
    #define INF 0x3f3f3f3f
    #define maxn 100005
    int n,m,k,t,ans,flag,max1,cnt,point;
    int dis[maxn],vis[maxn],head[maxn],pre[maxn];
    struct node{
        int v,next,w;
    }edge[maxn*20];
    void init(){
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)
        pre[i]=i;
        cnt=flag=0;
        max1=0;
    }
    int find(int a){
        if(pre[a]==a)
        return a;
        return pre[a]=find(pre[a]);
    }
    void add(int u,int v,int w){
        edge[++cnt].v=v;
        edge[cnt].w=w;
        edge[cnt].next=head[u];
        head[u]=cnt;
    }
    void dfs(int u){//求以u为根节点的子树中离点u最远的点已经最大距离 
        vis[u]=true;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            int w=edge[i].w;
            if(!vis[v]){
                dis[v]=max(dis[v],dis[u]+w);
                if(dis[v]>max1){
                    point=v;
                    max1=dis[v];
                }
                dfs(v);
            }
        }
    }
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF){
            init();
            int u,v,w;
            for(int i=1;i<=m;i++){
                scanf("%d%d%d",&u,&v,&w);
                if(flag)
                continue;
                int x=find(u);
                int y=find(v);
                if(x==y)
                flag=1;
                else
                {
                    pre[x]=y;
                    add(u,v,w);
                    add(v,u,w);
                }
                
            }
            if(flag){
                printf("YES
    ");
                continue;
            }
            int ans=0;
            memset(vis,0,sizeof(vis));
            queue<int>q;//用队列来存第一次dfs找出的所有点 
            for(int i=1;i<=n;i++){
                if(vis[i])
                continue;
                dfs(i);
                q.push(point);
                max1=0;
            }
            memset(dis,0,sizeof(dis));
            memset(vis,0,sizeof(vis));
            while(!q.empty()){
                point=q.front();
                q.pop();
                max1=0;
                dfs(point);
                ans=max(ans,max1);
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    Object转bigdecimal
    如何在A用户下建立视图,这个视图是A的表与B的表进行关联的?
    java.util.Date和java.sql.Date的区别和相互转化(转)
    JAVA如何获取GUID
    详解Oracle DELETE和TRUNCATE 的区别(摘)
    分开显示Excel2010打开的文档
    CentOS实验三:使用安装光盘建立本地软件源
    基本C库函数
    Shell_2(验证符合的输入)
    Shell_1(目录之间切换执行脚本)
  • 原文地址:https://www.cnblogs.com/6262369sss/p/10016190.html
Copyright © 2011-2022 走看看