hdoj 4514 并查集 树的直径

和我坐同桌的东神死活不让我把代码贴到他博客上- -

于是我就自己弄一个,仅此纪念我开通博客的第一篇文章。

可怜的等三天辛苦死我了。

正题开始

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

题目大意:

设计风景线,使风景线越长越好
给定的图,如果含有环则输出“YES”,如果不含有环则输出最大的距离。
图为无向图,不会出现重复输入。所以可以放心的使用并查集判断是否存在环
当计算最大距离时,就变成了,无向无环图求最长路径。(树求直径)
图中可能不止一颗树,所以需要计算每颗树的直径,从而求出图的最长路径
计算树的直径使用两次遍历的算法
第一次以树中任意节点为根节点遍历树,求出最大距离的节点p。
第二次以p为根节点遍历树,求出最大距离,此时的最大距离就为树的直径。
#include<stdio.h>
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include<string.h>
#pragma comment(linker,"/STACK:102400000,102400000")//设置栈大小
#define MAX 100001
using namespace std;
//自定义结构体Node存放尾,和值
typedef struct Node{
    int end;
    int value;
}tmpNode;
//已vector 形式存放Node 定义结构体NodeList
typedef vector<Node> NodeList;
//
NodeList nodeList[MAX];
//存放孤立节点和叶子节点的结构体
typedef vector<int> LInt;
LInt starts;
int father[MAX],save[MAX],dis[MAX],vist[MAX];
int n,m;
//初始化father,save,图;
void init(){
    int i;
    for(i=0;i<=n;i++)
    {
        nodeList[i].clear();
        father[i]=i;
        save[i]=0;
    }
     //存放孤立点和叶子点的表清空
    starts.clear();
}
//每次遍历之后都需要重置dis vist
void reset(){
    int i;
for(i=0;i<=n;i++)
    {
       dis[i]=0;
       vist[i]=0;
    }
}
//并查集find操作,查找x的祖先,并使用save数组压缩路径
int find(int x){
    int i=0,j=0;
    while(x!=father[x]){
        save[i++]=father[x];
        x=father[x];
    }
    for(j=0;j<i;j++){
    father[save[j]]=x;
    }
    return x;
}
//dfs遍历图,并计算跟节点到所有点的距离
//now存放当前节点,path为根节点到当前节点的距离
void dfs(int now,int path){
    int tpath=path,i,size;
    Node tmp;
    size=nodeList[now].size();
    for(i=0;i<size;i++){
        tmp=nodeList[now][i];
        if(!vist[tmp.end]){//如果节点end没有被访问过
            vist[tmp.end]=1;//访问end节点
            dis[tmp.end]+=path+tmp.value;//更新end节点到root的距离
            path=dis[tmp.end];//更新end节点到root的距离
            dfs(tmp.end,path);//遍历end的所有孩子节点
            path=tpath;             //重置path
            vist[tmp.end]=0;//重置end的访问
        }
    }
}
int main(){
    int flage,i,x,y,value,s1,s2;
    Node node;
    while(scanf("%d%d",&n,&m)!=EOF){
            flage=0;//标识是否存在环
            //如果m>=n则肯定存在环,只需继续输入数据
            if(m>=n){flage=1;for(i=0;i<m;i++)scanf("%d%d%d",&x,&y,&value);}
            else{
                init();//初始化所需函数
                for(i=0;i<m;i++){
                    scanf("%d%d%d",&x,&y,&value);
                    if(!flage){
                        s1=find(x);
                        s2=find(y);
                        if(s1==s2)flage=1;//如果s1=s2则存在环
                        else{
                                father[s1]=s2;//并查集union简化操作,此题中可以不用按秩和并
                                node.value=value;
                                node.end=y;
                                nodeList[x].push_back(node);//存入x到y的边
                                node.end=x;
                                nodeList[y].push_back(node);//存入y到x的边
                        }
                    }
                }
            }
            if(flage){printf("YES\n");continue;}
            int size,max,tmp,index,p,tmp1;
            //如果图中在以i为起点边小于等与1,则其为叶子节点或者孤立节点
            for(i=1;i<=n;i++){
                if(nodeList[i].size()<=1)starts.push_back(i);
            }
            size=starts.size();
            max=0;
            for(i=1;i<=n;i++){
                //如果father[i]=i,则进行dfs,此举是为了计算森林中每个树的最远路径
                //通过两次dfs找到树中的最大路径。
                if(father[i]==i){
                    reset();
                    vist[i]=1;//设置i为被访问
                    dfs(i,0);//以i为根节点dfs遍历图
                    tmp=0;
                    index=0;
                    for(p=0;p<size;p++){//遍历叶子节点和孤立节点找到最大的节点
                        tmp1=dis[starts[p]];
                        if(tmp1>tmp){
                        tmp=tmp1;index=starts[p];
                        }
                    }
                    //第一次遍历找到距离i最远的点index;
                    //以index为跟节点遍历图找到最远的点就为树中最远的路径
                    reset();
                    vist[index]=1;//设置index为被访问
                    dfs(index,0);//以index为跟节点遍历图
                    for(p=0;p<size;p++){//遍历叶子节点和孤立节点找到最大的节点
                      tmp1=dis[starts[p]];
                        if(tmp1>tmp){
                        tmp=tmp1;index=starts[p];
                        }
                }
                //图中每棵树中都存在一条最远路径,所以找出图中最大的路径还需要进行比较
                if(tmp>max)max=tmp;
            }
        }
            printf("%d\n",max);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值