求树的直径+并查集(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\n");
            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\n",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\n");
            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\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/6262369sss/p/10016190.html

时间: 2024-08-27 12:22:56

求树的直径+并查集(bfs,dfs都可以)hdu4514的相关文章

2017端午欢乐赛——Day1T3(树的直径+并查集)

//前些天的和jdfz的神犇们联考的模拟赛.那天上午大概是没睡醒吧,考场上忘了写输出-1的情况,白丢了25分真是**. 题目描述     小C所在的城市有 n 个供电站,m条电线.相连的供电站会形成一个供电群,那么为了节省材料,供电群是一棵树的形式,也即城市是一个森林的形式(树:V个点,V-1条边的无向连通图,森林:若干棵树).每个供电群中不需要所有供电站都工作,最少只需要一个工作,其余的就都会通过电线收到电,从而完成自己的供电任务.当然,这样会产生延迟.定义两个供电站的延迟为它们之间的电线数量

codeforces 455C C. Civilization(树形dp+树的直径+并查集)

题目链接: codeforces 455C 题目大意: 给出一些点,他们之间初始存在一些边,给出两种操作,第一种是查询某个点所在的树的直径,另一种是将两个树合并,要求使合并后的树的直径最小. 题目分析: 首先算取没做操作前的连通块里的树的直径,也就是先dfs一遍,找到深度最大的点,然后从这个点再搜,找到的最远的距离就是这棵树的直径,因为可以证明从根搜深度最大的点一定是树的直径的一个端点,因为它可以通过到达次大的深度的点或者找到与它公共祖先不在根处的获得树的直径. 然后每次合并,我们可以知道得到的

hdu 4514(树的直径+并查集)

湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 3930    Accepted Submission(s): 700 Problem Description 随着杭州西湖的知名度的进一步提升,园林规划专家湫湫希望设计出一条新的经典观光线路,根据老板马小腾的指示,新的风景线最好能建成环形,如果没有条件建成环形,

Codeforces 455C Civilization 树的直径+并查集

题目链接:点击打开链接 题意: 给定n个点 m条无向边的图 k个询问 无重边.自环.环 定义 2个点属于同国家:当这两个点连通时即这两个点是属于一个国家 操作1 x:输出x所在的国家内的最长路长度 操作2 x y:若x y属于一个国家 则忽略 若不属于一个国家,则联合这两个国家(2个集合间连一条边.使得连完后最长路最短) 连2个集合的最长路一定是 找2个集合最长路的中点进行连接 则连接后的最长路长度为 path[x]/2 + path[y]/2 +1 (除2向上取整) 然后给每棵树预处理出树的直

Codeforces 455C Civilization:树的直径 + 并查集【合并树后直径最小】

题目链接:http://codeforces.com/problemset/problem/455/C 题意: 给你一个森林,n个点,m条边. 然后有t个操作.共有两种操作: (1)1 x: 输出节点x所在树的直径. (2)2 x y: 如果x,y不在同一棵树上的话,用一条边连接x,y所在的树,并且使得到的新树的直径尽可能小. 题解: 首先对于初始状态,算出每一棵树的直径d[find(i)]. 每次合并树的时候,因为要尽可能让新树直径变小,所以显然这条边要分别连接两棵树直径的“中点”. 所以新树

求树的直径【两遍BFS】

两遍BFS.从任意一个点出发,第一遍可以找到直径的一端,从这端出发即可找到另外一端. 证明:从U点出发,到达V[画个图便清晰了] 1.如果U在直径上,则V一定是直径的一个端点. 2.如果U不在直径上.U,V线一定和直径有交点(如果没有交点,从U引一条路到直径交于U'.[反证]).有交点则V一定是直径另一端. 代码:[举例] int path(int x){ //从x出发,求直径 mem(vis,-1); while(!Q.empty()) Q.pop(); Q.push(x); vis[x]=0

hdu 4514 湫湫系列故事――设计风景线(求树的直径)

随着杭州西湖的知名度的进一步提升,园林规划专家湫湫希望设计出一条新的经典观光线路,根据老板马小腾的指示,新的风景线最好能建成环形,如果没有条件建成环形,那就建的越长越好.  现在已经勘探确定了n个位置可以用来建设,在它们之间也勘探确定了m条可以设计的路线以及他们的长度.请问是否能够建成环形的风景线?如果不能,风景线最长能够达到多少?  其中,可以兴建的路线均是双向的,他们之间的长度均大于0. Input 测试数据有多组,每组测试数据的第一行有两个数字n, m,其含义参见题目描述:  接下去m行,

分别利用并查集,DFS和BFS方法求联通块的数量

联通块是指给定n个点,输入a,b(1<=a,b<=n),然后将a,b连接,凡是连接在一起的所有数就是一个联通块: 题意:第一行输入n,m,分别表示有n个数,有输入m对连接点,以下将要输入m行(输入数据到文件截止): 输出:第一行要求输出联通块的个数,并在第二行分别输出每个联通块中点的数量,每个数之间以一个空格隔开. 样例 15 31 42 53 5输出:2 2 3样列2 9 81 22 33 43 74 54 67 87 9输出: 19 如果不明白的话可以画图试试,最多花半个小时,要是早这样不

ural 1145 Rope in the Labyrinth 图中 bfs求树的直径

1145. Rope in the Labyrinth Time limit: 0.5 second Memory limit: 64 MB A labyrinth with rectangular form and size m × n is divided into square cells with sides' length 1 by lines that are parallel with the labyrinth's sides. Each cell of the grid is