P1395 会议
思路: 读题后感觉是树的重心,但又不确定,所以查了下重心的性质:
定义: 树的重心也叫树的质心。对于一棵树n个节点的无根树,找到一个点,使得删除这个点后最大连通块(一定是树)的结点数最小,那这个点就是树的重心。
重心性质:
1、树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个重心,他们的距离和一样。
2、把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
3、一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
4、一棵树最多有两个重心,且相邻。
所以这是个树的重心裸题。
Code 1:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
vector<int> G[N];
int son[N]; //每个节点有多少孩子
int maxx[N]; //存 删除每个节点后 剩下的连通块里最大的连通块大小
int ans = N;
int n,root = N;
void DFS(int x,int f)
{
for(auto v : G[x]){
if(v==f) continue;
DFS(v,x);
son[x] += son[v] + 1;
maxx[x] = max(maxx[x],son[v]+1); //孩子中最大的一个(一个孩子是一个连通块)
}
maxx[x] = max(maxx[x],n-son[x]-1); //另一个连通块
}
int main()
{
cin>>n;
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
}
DFS(1,0);
for(int i=1;i<=n;i++) //一定不能在递归里找最小根,,,
if(maxx[i] < ans) ans = maxx[i] , root = i; //从小到大遍历保证 root 最小
memset(son,0,sizeof son);
DFS(root,0); //以此为跟再次计算每个结点的孩子个数
long long cnt =0;
for(int i=1;i<=n;i++) cnt += son[i]; //总和就是步数
cout<<root<<" "<<cnt<<endl;
return 0;
}
Code 2:
数组模拟 vector 邻接表,访问较快,就是费数组;
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
int h[N],e[N*2],ne[N*2],idx; //数组模拟vector的邻接表,访问速度较快;
int son[N];
int maxx[N];
int n;
bool st[N]; //每个节点只访问一遍
void add(int a,int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx ++;
}
void DFS(int x)
{
st[x] = 1; //也可以不用标记数组,递归参数加上 f 父节点即可
for(int i = h[x];i!=-1;i = ne[i]){
int v = e[i];
if(st[v]) continue;
DFS(v);
son[x] += son[v] + 1;
maxx[x] = max(maxx[x],son[v]+1);
}
maxx[x] = max(maxx[x],n-son[x]-1);
}
int main()
{
memset(h,-1,sizeof h); //一定不要忘了,没开玩笑,,
cin>>n;
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
add(x,y) , add(y,x);
}
DFS(1);
int root , ans = N;
for(int i=1;i<=n;i++)
if(maxx[i] < ans) ans = maxx[i] , root = i; //从小到大遍历保证 root 最小
memset(son,0,sizeof son);
memset(st,0,sizeof st);
DFS(root);
long long cnt =0;
for(int i=1;i<=n;i++) cnt += son[i];
cout<<root<<" "<<cnt<<endl;
return 0;
}