1. 程式人生 > >求樹的直徑+並查集(bfs,dfs都可以)hdu4514

求樹的直徑+並查集(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;
}