求树的直径+并查集(bfs,dfs都可以)hdu4514
Posted 6262369sss
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求树的直径+并查集(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; }
以上是关于求树的直径+并查集(bfs,dfs都可以)hdu4514的主要内容,如果未能解决你的问题,请参考以下文章