屠龙者(最大子树)

题目描述

  小明是A村里的屠龙者,他一直保卫着村子的和平,以不受恶龙的侵扰。而恶龙们也对小明恨之入骨,于是恶龙们决定组织一次集体进攻,以打败小明,拿下A村。小明知道,恶龙集体进攻的时候,会在彼此之间建立一种神秘的链接,而被这种链接连接起来的恶龙能够增长彼此的能力,且每有一只恶龙加入到一个链接中,这个链接里的所有龙的能力都会加1,而只有当小明的战斗力大于龙的战斗力时,才能将龙杀死。万幸的是,小明有一把一次性的屠龙刀,他可以无视战斗力地杀死一只龙,并消除这条龙身上的所有链接。假设每条龙不被链接时的战斗力为1,初始时所有N只恶龙被N-1条链接连接在一起。小明想知道他至少要有多少的战斗力,才能将所有龙都杀死,同时他想知道,他应该用屠龙刀杀掉哪只龙。

输入描述

  输入的第一行是一个整数N(1<=N<=40000), 表示一共有N只龙。接下来N-1行整数对a,b(以空格分隔),表示龙之间的链接关系。

输出描述

  输出以空格分隔的两个整数。第一个整数X,表示应用屠龙刀杀死的龙的编号。若有多只龙都可被屠龙刀杀死,输出编号最小的那个第二个整数T,表示小明至少需要有的战斗力。

示例

输入
8
1 2
2 3
1 5
5 6
6 8
2 4
5 7
输出
1 5

解题思路

  模拟给出的示例,当杀死某条龙,也就是删除某一个节点之后,那么剩下的节点按照被杀死的节点的邻节点分成了几个集合,那么最大的集合(节点数最多)的节点数加1,就是屠龙者至少要拥有的能力。所以本质上要求所有节点的所有子树中最大的那个子树。
  如果是遍历某一个节点,分别求出最大子树,由于存在大量的冗余计算,一定会超时。由于题目中明确初始N个节点通过N-1条边链接在一起,所以初始就可以看作一棵树,自己指定一个根节点之后(相当于给出了方向,将无向图变成了有向图),就可以深搜求出每一个节点作为根节点的子树所包含的节点数量(所有子树大小和+1),因为加上了方向性,还有一个子树没有办法直接求出,用总的节点数-该节点作为根节点的子树的大小,就是剩下的那个子树的大小,在这个过程中在维持一个最小的最大子树记录就可以了。

实现代码

#include <iostream>
#include <vector>
#define NN 40010
using namespace std;
vector<vector<int> > Long(NN,vector<int>());
int n,maxn=NN,index_max=NN;
int getTreeSum(int index,int pre){
	int len=Long[index].size();
	int sum=0,maxv=0,subsum;
	for(int i=0;i<len;i++)
		if(Long[index][i]!=pre){
			subsum=getTreeSum(Long[index][i],index);
			if(subsum>maxv)
				maxv=subsum;
			sum+=subsum;
		}
	sum++;
	int left=maxv;
	if(n-sum>maxv)
		left=n-sum;
	if(maxn>left){
		maxn=left;
		index_max=index;
	}else if(maxn==left&&index_max>index)
		index_max=index;
	return sum;
}
int main(){
	cin>>n;
	int a,b;
	for(int i=1;i<n;i++){
		cin>>a>>b;
		Long[a].push_back(b);
		Long[b].push_back(a);

	}
	getTreeSum(1,-1);
	if(maxn!=0)
		maxn++;
	cout<<index_max<<" "<<maxn<<endl;
	return 0;
}

  最初所有节点链接在一起这个条件将复杂度降低了,否则需要两次深搜。

发布了16 篇原创文章 · 获赞 0 · 访问量 319

猜你喜欢

转载自blog.csdn.net/qq_41922018/article/details/104383175