【洛谷】P5536 题解(树的直径,贪心)

【洛谷】P5536 题解(树的直径,贪心)

题目描述

X X X 国有 n n n 座城市, n − 1 n - 1 n1 条长度为 1 1 1 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达。显然,城市和道路形成了一棵树。

X X X 国国王决定将 k k k 座城市钦定为 X X X 国的核心城市,这 k k k 座城市需要满足以下两个条件:

  1. k k k 座城市可以通过道路,在不经过其他城市的情况下两两相互到达。
  2. 定义某个非核心城市与这 k k k 座核心城市的距离为,这座城市与 k k k 座核心城市的距离的最小值。

那么所有非核心城市都有一个自己距离核心城市的距离 d i s i dis_{i} disi 。非核心城市到达核心城市的距离的最大值 d i s m a x = M a x ( d i s i ) dis_{max} =Max(dis_i) dismax=Max(disi) ,求出 d i s m a x dis_{max} dismax 的最小值。

输入格式

第一行:2个正整数 n n n , k k k

接下来 n − 1 n-1 n1 行,每两行 2 2 2 个正整数 u u u . v v v ,表示第 u u u 座城市与第 v v v 座城市之间有一条长度为 1 1 1 的道路。

数据范围
  • 1 ≤ k < n ≤ 1 0 5 1 \le k < n \le 10^5 1k<n105
  • 1 ≤ u , v ≤ n , u ≠ v 1\le u,v \le n, u\neq v 1u,vn,u=v ,保证城市与道路形成一棵树。
输入输出样例

输入 #1

6 3
1 2
2 3
2 4
1 5
5 6

输出 #1

1

解题思路

简化问题

首先考虑如果只能建立 1 1 1 座核心城市,很容易得出,这个核心城市只能在 树的直径的中点

思路
  1. 由于 k k k 座核心城市必须 挨在一起 (条件1)。问题可以变成: 先在 树的直径的中点 建立核心城市,然后 延申 k − 1 k-1 k1 座核心城市。

  2. 一座城市被立为核心城市的必要条件是:该城市在产生 d i s m a x dis_{max} dismax 的路径上

方法
  1. 先找出树的直径,根据树的直径找出树的中心
  2. 树的中心 跑一次 dfs ,维护 D e p [ x ] Dep[x] Dep[x] 。 ( D e p [ x ] Dep[x] Dep[x] 的值为: x x x 的子树节点到达 x x x 的距离的最大值 +1。意义是:如果 x x x 不是核心城市,那么其子树中的节点到达核心城市的距离的最大值)
  3. D e p Dep Dep 数组降序排序,输出 D e p [ k + 1 ] Dep[k+1] Dep[k+1] 即为答案。
代码
#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;

int dp[maxn];
int Dep[maxn];
int vis[maxn];
vector <int>p[maxn]; 
int n,k,a,b,Po1,Po_mid,Max_dep,d,ind;

void add_edge(int x,int y){
	p[x].push_back(y);
	p[y].push_back(x);
}

void Dp(int x,int fa){
	int i,j;
	for(i = 0; i < p[x].size(); i++){
		j = p[x][i];
		if(j == fa) continue;
		Dp(j,x);
		d = max (d, dp[x] + dp[j] + 1);
		dp[x] = max(dp[x], dp[j] + 1);
	}
}

void dfs1(int x,int fa,int dep){
	if(dep > Max_dep){
		Max_dep = dep;
		Po1 = x;
	}
	int i,j;
	for(i = 0; i < p[x].size(); i++){
		j = p[x][i];
		if(j == fa) continue;
		dfs1(j,x,dep+1);
	}
}

void dfs2(int x,int fa,int dep){
	Dep[x] = dep;
	if(Dep[x] == d){
		for(int k = 1; k <= n; k++)
			if(vis[k] && Dep[k] == d/2){
				Po_mid = k;
				break;
			} 
		return;
	}
	int i,j;
	for(i = 0; i < p[x].size(); i++){
		j = p[x][i];
		if(j == fa) continue;
		vis[j] = 1;
		dfs2 (j,x,dep+1);
		vis[j] = 0;
	} 
}

void dfs3(int x,int fa,int dep){
	Dep[x] = 1;
	int i,j;
	for(i = 0; i < p[x].size(); i++){
		j = p[x][i];
		if(j == fa) continue;
		dfs3 (j,x,dep+1);
		Dep[x] = max(Dep[x],Dep[j] + 1) ;
	}
}

bool cmp(int x,int y){return x > y;}

int main(){
	//freopen("P5536.in","r",stdin);
	//freopen("P5536.out","w",stdout); 
	scanf("%d%d",&n,&k);
	for(int i = 1; i < n; i++){
		scanf("%d%d",&a,&b);
		add_edge(a,b);
	}
	
	Dp(1,0);
	dfs1(1,0,0); 
	dfs2(Po1,0,0);
	dfs3(Po_mid,0,0);
	sort(Dep+1,Dep+1+n,cmp);
	printf("%d",Dep[k+1]);
	return 0;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anew·Hepcher

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值