题目描述
小明是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;
}
最初所有节点链接在一起这个条件将复杂度降低了,否则需要两次深搜。