洛谷题单指南-图论之树-P5536 【XR-3】核心城市
原题链接:https://www.luogu.com.cn/problem/P5536
题意解读:核心城市互相连通,确定k个核心城市,使得到核心城市距离最大值最小。
解题思路:
1、k个城市不好确定,可以先考虑1个
要求一个点,使得到其他点最大值最小,这是树的中心概念。
树的中心可以借助换根DP来求解:
第一次dfs:求出每个节点所在子树能到达的最远距离d1和次远距离d2,并记录路径
第二次dfs:求出每个节点不往子树走而是往上走能到的最远距离up
枚举每一个点,根据max(d1[i], up[i])求得一个min值,这时的i即是中心。
2、要选择k个核心城市,中心必选
证明:如果不选中心,中心到非核心点的距离是最大值中最小的,必有一条路径超过中心能到的最远距离,不是最佳答案。
3、第一核心城市选中心,第二城市选择与核心城市邻接的,且能达到距离最远的点
证明:如果不选能到达距离最远的点,必有一条经过该点路径超过到核心城市的最远距离,不是最佳答案。
4、从中心开始,如上选择k个点,剩下的核心点的邻接点中,能到距离最远的点就是核心城市能到的最远距离经过的点
用优先队列可以实现从中心点不断扩展选择k个能到达距离最远的点,第k+1个点能到达的最远距离再加1就是答案。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005, INF = 0x3f3f3f3f;
vector<int> g[N];
int d1[N], d2[N], up[N]; //节点往子树能到的最大深度d1、次大深度d2、往父节点方向能到的最大深度up
int to1[N], to2[N]; //在最大深度路径上的下一个节点to1、次大深度路径上的下一个节点to2
priority_queue<pair<int, int>> q; //{节点能到的最远距离, 节点编号}
bool vis[N];
int n, k;
void dfs1(int u, int p)
{
for (auto v : g[u])
{
if (v == p) continue; // 跳过父节点
dfs1(v, u); // 先递归处理子节点
int d = d1[v] + 1; // 当前子树的深度
if (d >= d1[u]) // 如果比最大深度大
{
d2[u] = d1[u]; // 原来的最大变为次大
to2[u] = to1[u]; // 对应的节点也要更新
d1[u] = d; // 更新最大深度
to1[u] = v; // 更新最大深度路径上的下一个节点
}
else if (d > d2[u]) // 如果比次大深度大但不超过最大深度
{
d2[u] = d; // 更新次大深度
to2[u] = v; // 更新次大深度路径上的下一个节点
}
}
}
void dfs2(int u, int p)
{
for (auto v : g[u])
{
if (v == p) continue; // 跳过父节点
// 计算子节点v的up值
if (to1[u] == v) // 如果v是u的最大深度路径上的下一个节点
{
// 则v向上走的最大深度是max(u的次大深度+1, u的up+1)
up[v] = max(d2[u] + 1, up[u] + 1);
}
else // 如果v不是u的最大深度路径上的下一个节点
{
// 则v向上走的最大深度是max(u的最大深度+1, u的up+1)
up[v] = max(d1[u] + 1, up[u] + 1);
}
dfs2(v, u); // 递归处理子节点
}
}
int solve(int dist, int center)
{
//从中心开始,依次选取能到达距离最远的领接点,直到k个
//队列里剩下的第一个就是距离核心城市最远的路径上的点
int cnt = 0;
q.push({dist, center});
while(k--)
{
pair<int, int> p = q.top(); q.pop();
int u = p.second;
vis[u] = true;
for(auto v : g[u])
{
if(vis[v]) continue;
q.push({d1[v], v});
}
}
return q.top().first + 1;
}
int main()
{
cin >> n >> k;
for(int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1, 0);
dfs2(1, 0);
int center = 0; //树的中心
int dist = INF; //中心能到的最远距离
for(int i = 1; i <= n; i++)
{
int t = max(d1[i], up[i]);
if(t < dist) dist = t, center = i; //找到树的中心
}
memset(d1, 0, sizeof(d1)); memset(d2, 0, sizeof(d2));
memset(to1, 0, sizeof(to1)); memset(to2, 0, sizeof(to2));
dfs1(center, 0); // 以中心节点为根重新计算每个节点的最大深度
cout << solve(dist, center) << endl;
return 0;
}
浙公网安备 33010602011771号