pat 1021

先用一次DFS来判断联通分量的个数(在网上看到很多题解用的是并查集来判断,以后学习一下),同时通过这次DFS找到距离最短的一个点


证明之前,先介绍两个概念,一个是直径,树的deepest root到最远叶子的路径我们暂称直径,而deepest root和最远叶子节点则成为端点。 注明关键在于以下两点:

  • 从任意一个点dfs得到的最远点都是直径的端点。证明如下:

    任意选一点A,做dfs查找距离A最远的点,因为是dfs,所以肯定会经过某直径上的点B,从B出发最远的点是直径上的端点C。 那么,这个端点C为何一定是A的最远居里点呢?可以反证证明,如果有一个点D,A->D不经过直径,且length(A->D) > length(A->C), 那么,从C所在直径上构造出经过D的比原直径更长的直径,得证。

  • 所有的树的直径都交于一点(或者公用段路径上的几点)。 于是,从B点dfs出发的所有最长距离的点即为deepest root。证明如下:

    如果两条直径不相交,而树上任意两点肯定连通,则从两直径上,可以选取两点连通以组成更长的直径;如果三条直径相较于不同的两点,也可以根据相交截断的长度组合出更长的直径。

这个是网上对于这种算法给出的证明,要指出的是两次DFS是没有办法完全实现的,例如形如>-<这种类型的树,连字符两端的端点可以通过第二次DFS找到,但是,连字符同一侧的则需要再进行一次DFS
#include<stdio.h>
#include<stdlib.h>

int graph[10000][10000];
int longest[10000],length,dp;
int d[10000];
void deep(int n,int u,int height){
	int i,j;
	int flag = 0;
	for(i = 0;i<n;i++)
		if(i!=u && graph[u][i] && !d[i]){
			d[i] = 1;
			deep(n,i,height+1);
			flag = 1;
		}
	if(flag == 0) 
		if(height == dp)
		longest[length++] = u;
		else if(height>dp){
			dp = height;
			longest[0] = u;
			length = 1;
		}

}
int DFS(int n,int u){
	int i,j;
	for(i = 0;i<n;i++)
		d[i] = 0;
	int NumOfComp = 0;
		d[u] = 1;
		//dp = 0;
		deep(n,u,0);
		NumOfComp++;
	for(i = (u+1)%n;i!=u;++i,i = i%n)
		if(!d[i]){
			NumOfComp++;
			d[i] = 1;
			deep(n,i,0);
		}
		return NumOfComp;
}
int cmp(const void*a,const void*b)
{
return*(int*)a-*(int*)b;
}
int main(){
	int i,j;
	int n,NumOfComp = 0;
	freopen("1.in","r",stdin);
	scanf("%d",&n);
	for(int k = 0;k<n-1;k++){
		scanf("%d%d",&i,&j);
		i--;
		j--;
		graph[i][j] = graph[j][i] = 1;
	}
	NumOfComp = DFS(n,0);
	if(NumOfComp>1)
		printf("Error: %d components\n",NumOfComp);
	else{
		int sp = longest[0];
		DFS(n,sp);
		longest[length++] = sp;
		DFS(n,longest[0]);
		qsort(longest,length,sizeof(int),cmp);
		for(i = 0;i<length;i++)
		{	
			if(i&&longest[i] == longest[i-1])
				continue;
			printf("%d\n",longest[i]+1);

		}
	}

return 0;}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值