树的直径和数量 hdu 3534

题意:给定一棵树,需要我们求得树的直径 和 直径的数量

 

思路:

树的直径的求法:

1.两遍dfs,这种方法适合只求 长度数值

 2.记录每个节点的  最长叶节点  和  次长叶节点(可能不存在)  的距离

 

树:-> 递归求解(子树分解)【所以,不能再返回父节点,遍历的时候记得删掉父节点

 

1.考虑一下直径可能存在哪里:

1.经过我们设定的根节点

2.不经过我们设定的根节点(一开始没有考虑),但是直径一定是经过某个子树的根节点 所以需要记录所有结点的 次长 和 最长叶节点 距离

 

所以我们将问题转换为,必须经过我们子树的根节点的直径有多少?

如何保证一定经过子树的根节点? 我们遍历子树的所有分支的根节点,将其 最长叶节点距离  作为根节点的  最长叶节点距离 或者 次长叶节点距离,这样就可以保证一定是经过 子树的根节点的

 

2.分类统计各种情况下,两个不同子树的最长距离和数量

 

 

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
#define rep(i,a,b) for(int i=a;i<b;++i)

const int N=10010;
struct Edge{
	int v,w;
	Edge(int _v=0,int _w=0){
		v=_v,w=_w; 
	} 
};

vector<Edge>edge[N];

void add_edge(int u,int v,int w){
	edge[u].push_back(Edge(v,w));
	edge[v].push_back(Edge(u,w));
}

int path[N],num[N];//
int maxDis[N],secDis[N];
int maxNum[N],secNum[N];

void dfs(int x,int f){
	if(edge[x].size()==1&&edge[x][0].v==f){//防止一开始就是叶节点,直接退出
		maxDis[x]=secDis[x]=0;
		
		maxNum[x]=num[x]=1;
		path[x]=0;		
		return;
	}
	
	rep(i,0,edge[x].size()){
		Edge& e=edge[x][i];
		if(e.v==f)continue;
		
		dfs(e.v,x);
		if(maxDis[e.v]+e.w>maxDis[x]){
			secDis[x]=maxDis[x];
			secNum[x]=maxNum[x];
			 
			maxDis[x] = maxDis[e.v]+e.w;
			maxNum[x] = maxNum[e.v];
		}else if(maxDis[e.v]+e.w==maxDis[x]){
			maxNum[x] += maxNum[e.v];
		}else if(maxDis[e.v]+e.w>secDis[x]){
			secDis[x] = maxDis[e.v]+e.w;
			secNum[x] = maxNum[e.v];
		}else if(maxDis[e.v]+e.w == secDis[x]){
			secNum[x] += maxNum[e.v];
		}
	}
	
	int c1=0,c2=0;
	rep(i,0,edge[x].size()){
		Edge & e=edge[x][i];
		if(e.v==f)continue;
		
		if(maxDis[x]==maxDis[e.v]+e.w){
			c1++;
		}else if(secDis[x]==maxDis[e.v]+e.w){
			c2++;
		}
	}
	
	//printf("** x:%d c1:%d c2:%d secDis:%d\n",x,c1,c2,secDis[x]);
	 
	if(c1>1){//多个分支都有最大距离
		path[x]=maxDis[x]*2;
		int sum=0;
		rep(i,0,edge[x].size()){
			Edge& e=edge[x][i];
			if(e.v==f)continue;
			
			if(maxDis[x]==maxDis[e.v]+e.w){
				num[x]+=sum*maxNum[e.v];
				sum+=maxNum[e.v];
			}	
		}
	}else if(c1==1&&c2>0){//只有一个最大距离,多个次大距离
		path[x]=maxDis[x]+secDis[x];
		num[x]=maxNum[x]*secNum[x];
	}else{//只有一个最大距离,x是直径的一端
		path[x]=maxDis[x];
		num[x]=maxNum[x];
	}
}

/*

*/

int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
		
		rep(i,1,n+1){
			edge[i].clear();
			num[i]=path[i]=maxNum[i]=secNum[i]=maxDis[i]=secDis[i]=0;
		}
		
		rep(i,0,n-1){
			int u,v,w;
			scanf("%d %d %d",&u,&v,&w);
			add_edge(u,v,w);
		}
		dfs(1,0);

	//	rep(i,1,n+1)printf("*%d maxDis:%d secDis:%d path:%d num:%d mxNm:%d\n",i,maxDis[i],secDis[i],path[i],num[i],maxNum[i]);
		
		int mxDs=0,res=0;
		rep(i,1,n+1){
			if(path[i]>mxDs){
				mxDs=path[i];
				res=num[i];
			}else if(path[i]==mxDs){
				res+=num[i];
			}
		} 
		
		if(n==1)res=1;
		printf("%d %d\n",mxDs,res);
	}
	return 0;
}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值