洛谷P1395 会议(图论,树形DP)
题目描述
有一个村庄居住着n个村民,有n-1条路径使得这n个村民的家联通,每条路径的长度都为1。现在村长希望在某个村民家中召开一场会议,村长希望所有村民到会议地点的距离之和最小,那么村长应该要把会议地点设置在哪个村民的家中,并且这个距离总和最小是多少?若有多个节点都满足条件,则选择节点编号最小的那个点。
输入格式
第一行。一个数n,表示有n个村民。
接下来n-1行,每行两个数字a和b,表示村民a的家和村民b的家之间存在一条路径。
输出格式
一行输出两个数字x和y
x表示村长将会在哪个村民家中举办会议
y表示距离之和的最小值
输入
4
1 2
2 3
3 4
输出
2 4
具体代码(注:缝的洛谷大佬Y_B_Y代码,感兴趣的兄弟可以去该题题解区找了看)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ll long long
using namespace std;
const int N = 5e4+5;
struct edge
{
int t, nex;
}e[N<<1];
int n, head[N], d[N], ct[N], fa[N];
//d[]储存距离之和 ct[]储存子树的结点数 fa:father
int tot, ans, minn = 1e9+7;
void add(int x,int y)//add edge
{
e[++tot].t = y;
e[tot].nex = head[x];
head[x] = tot;
}
void dfs(int x,int st)
{
for(int i=head[x];i!=0;i=e[i].nex)//visit all edges that from x
{
if(e[i].t!=fa[x])//该边没有指向父结点
{
++ct[x];// 以x为根的子树,树的总结点数加一
fa[e[i].t] = x;//确认x孩子结点的父结点为x
dfs(e[i].t,st+1);//遍历子树
ct[x] += ct[e[i].t];//用x的子树的子树去修改值
}
}
d[1] += st;//记录所有点到1的距离之和
}
void fd(int x)//进行树状DP!
{
for(int i=head[x];i!=0;i=e[i].nex)
{
if(e[i].t!=fa[x])
{//关键:d[child] = d[father] - (child's nodes) + (other nodes)
d[e[i].t] = d[x] - ct[e[i].t] - 1 + (n - ct[e[i].t] - 1);
//意思:所有child的子树结点与child的距离减少1,其余结点增加1
fd(e[i].t);//递归处理剩下的点
}
}
return;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;++i)
{
int a, b;
scanf("%d %d",&a,&b);
add(a,b);
add(b,a);
}
dfs(1,0);//默认以1为该树的根
fd(1);//从根1开始,推得各点的D值
for(int i=1;i<=n;++i)
{
if(d[i]<minn)//遍历所有节点,寻找到距离之和最小的点
{
minn = d[i];
ans = i;
}
}
printf("%d %d\n",ans,minn);
return 0;
}