您的位置:首页 > 其它

2019牛客暑期多校训练营(第四场)A

2019-08-01 00:35 555 查看

题意:给定n个顶点,n-1条边权为1的边,将各个顶点连成一个最小生成树,再给定一个K,表示有多少个人,每个人都在特点的一个顶点上,现在这些人要相会,求使得这些人能够相聚在一起的最短时间。
看了标程,答案就是K个人当中那2个距离最远的人的距离d,答案就是d/2向上取整。
证明:
必要性:K个人当中,最远的那2个人u,v要相遇,则其中一个必定至少要走⌈d/2⌉的步数。
充分性:我们取最远那2个人u,v相会的那个点,这个点一定是该路径上的中点或中点附近,设该点为point。假若有另外一个人k,k到point的距离大于⌈d/2⌉,那么u,v这两个人的距离就不是所有人中最远的了,这样就与假设矛盾。

综上,命题成立。
那么这么一转化,其实本题求的就是一颗树的直径。
什么是树的直径?树的直径就是:树中距离最远的两个节点的距离。怎么求树的直径呢? 方法就是:先在树中任意找一节点设为根v1,对其求BFS,求出v1到其它所有节点的距离,取其中最远距离的节点为v2,再设v2为根,对其求BFS,求出v2到其它所有节点的距离,取其中最远距离的节点v3,那么v2与v3的距离就是树的直径了——两遍BFS遍历整棵树就可以求出其直径。
关于树的直径,还可参考以下博客:
https://www.cnblogs.com/Khada-Jhin/p/10195287.html (树的直径性质及算法证明)
https://www.geek-share.com/detail/2554651640.html
代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int inf=0x7f7f7f7f;
int head[maxn],cnt;
int n,K,k[maxn];
int dis[maxn];
bool vis[maxn];
struct Road
{
int u,v,w,next;
Road(int u=0,int v=0,int w=0) { this->u=u; this->v=v; this->w=w; };
}r[2*maxn];

void addedge(int u,int v,int w)
{
r[cnt].u=u; r[cnt].v=v; r[cnt].w=w; r[cnt].next=head[u]; head[u]=cnt++;
r[cnt].u=v; r[cnt].v=u; r[cnt].w=w; r[cnt].next=head[v]; head[v]=cnt++;
}

void BFS(int s)
{
queue<int> Q;
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));		//WA了两次,因为把后面的sizeof(dis)写成sizeof(vis)...(捂脸)
Q.push(s);
vis[s]=1;
while(!Q.empty())
{
int u=Q.front(); Q.pop();
for(int i=head[u]; i ;i=r[i].next)
{
if(!vis[r[i].v])
{
dis[r[i].v]=dis[u]+1;		//1为边权,如果边权不为1,就修改为r[i].w就可以了
Q.push(r[i].v);
vis[r[i].v]=1;
}
}
}
}

int main()
{
scanf("%d%d",&n,&K);
memset(head,0,sizeof(head));
cnt=2;
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v,1);
}
for(int i=1;i<=K;i++)
scanf("%d",&k[i]);
BFS(k[1]);		//第一次BFS的起点是任选的,啥都行
int v1=0,temp=0;
for(int i=1;i<=K;i++)
{
if(temp<dis[k[i]])		//找到K个人当中,距离最远的那个v1
temp=dis[v1=k[i]];
}
BFS(v1);		//对v1进行BFS
int v2=0;	temp=0;
for(int i=1;i<=K;i++)
{
if(temp<dis[k[i]])		//找到与v1距离最远的节点v2
temp=dis[v2=k[i]];
}
printf("%d\n",(dis[v2]+1)/2);		//v1到v2的距离向上取整即可
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: