树的直径和重心

树的重心

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=50010;//常量 
struct use{
    int st,en;//st为起点,en 为终点 
}b[1000001];//存储边信息 
int son[N],n,ans[N],minn,next[5000001]={0},point[100001]={0},tot;
//son记录最大子节点树,下标表示结点的值 
bool f[N];
//通讯图的构建:创建边的起点和终点 
void add(int x,int y){
    tot++; //记录边的编号 
	next[tot]=point[x]; //表示与当前tot边同一起点x的上一条边的编号
	point[x]=tot; //point存储当前点的最后一条连接边 
    b[tot].st=x; //边的起点是x  
	b[tot].en=y; //边的终点是y 
	return;
}
//深搜:查找以结点x为起点的所有结点的子树结点数 
void dfs(int x){//x为结点值 
    int u,balance=0;
    son[x]=0;
    f[x]=false; //标记为false的点,避免双向边重复记录结点数 
    for(int i=point[x];i;i=next[i]){
        u=b[i].en;  //边的终点 
        if(!f[u]) 	//点u被标记为false 
			continue; //跳过本层循环,继续查找下一个子节点 
        dfs(u); //找到新的根节点,继续深搜 
        son[x]+=son[u]+1;   //子节点数=儿子的子节点数+1(本身)
        //balance表示以结点i为父亲的子树中,结点个数的最大值 
		balance=max(balance,son[u]+1);
    }
    //还有一棵由原根节点为根的树,它的节点数就是原树总结点数-(被取点的儿子树+1)
    balance=max(balance,n-son[x]-1);
    //ans存放重心结点编号,ans[0]记录重心的个数 
    if(balance<minn){
        minn=balance;
        ans[0]=1;
        ans[1]=x;
    }
    else if(balance==minn){
        ans[0]+=1;
        ans[ans[0]]=x;
    }
    return;
}
int main(){
    int x,y;
    cin>>n;
    minn=1000001;
    memset(f,1,sizeof(f)); //初始化函数,将f中的内容全部设置为1 
    for(int i=1;i<n;i++){ //输入n个点的(n-1)条边 
        cin>>x>>y; 
        //添加双向边 
        add(x,y);
        add(y,x);        
    }
    //从任意一个点开始深搜,进行每个结点的子树结点数【balance】统计 
    dfs(1);
    sort(ans+1,ans+ans[0]+1);
    for(int i=1;i<=ans[0];i++) 
      cout<<ans[i]<<" ";
    cout<<endl;
    return 0; 
}

/*
6
1 2
2 3
2 5
3 4
3 6
*/

树的直径

    #include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=100001;
//创建边结构体--有两个端点属性 
struct node{
    int to,next;//next 存从起点出发的上一条边的下标  to到那个结点 
}edge[maxn];
int cnt=1; //记录边的编号 
int vis[maxn],head[maxn],dis[maxn];
//添加边--传入边的起点和终点 
void add(int u,int v){
	cnt++; //记录边的编号 
    edge[cnt].to=v; //记录边指向点V 
    edge[cnt].next=head[u]; //***记录边的上一条边
    head[u]=cnt; //存储以u出发的新加入的边的编号 
}
void dfs(int x){
    vis[x]=1; //当前结点参与了深搜过程,则被标记 
    //循环执行以x为起点的所有条边-->从最后加入的边开始,到最先加入的边结束 
    for(int i=head[x];i;i=edge[i].next){
        if(!vis[edge[i].to]){ //如果这条边的终点未被记录过,则计数并继续深搜 
            dis[edge[i].to]=dis[x]+1; //当前点的边权加一 
            dfs(edge[i].to); //继续深搜 
        }
    }
}
int main(){
	//输入点的数量 
    int n;
    cin>>n;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(dis,0,sizeof(dis));
    //创建边 
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    dis[1]=1;
    //第一次深搜 
    dfs(1);
    int pos=1;
    //找到树直径的一个端点 
    for(int i=1;i<=n;i++){
        if(dis[i]>dis[pos])
        pos=i;
    }
    memset(vis,0,sizeof(vis));
    dis[pos]=1;
    //第二次深搜 
    dfs(pos);
    //找到另一个端点 ,以及两个端点之间的最远距离 
    int ans=0;
    for(int i=1;i<=n;i++){
       	if(dis[i]>ans)
           ans=dis[i];
    }
    cout<<ans;
    return 0; 
}

posted @ 2022-06-11 16:39  李墨韵  阅读(25)  评论(0编辑  收藏  举报