2019牛客多校第四场A meeting——树的直径
题意:
一颗 $n$ 个节点的树上标有 $k$ 个点,找一点使得到 $k$ 个关键结点的最大距离最小。
分析:
问题等价于求树的直径,最小距离即为直径除2向上取整。
有两种求法,一是动态规划,对于每个结点,把所有子结点的 $d(i)$ (表示根为 $i$ 的子树中根到叶子的最大距离)都求出来,设 $d$ 值前两大的结点为 $u$ 和 $v$,则 $d(u) + d(v) +2$就是树的直径。
另一种是两次DFS,从任意一个关键节点开始,找到离它最远的关键结点 $y$,从 $y$ 出发dfs找到新的最远结点 $z$,形成的就是直径。
代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; struct Edge { int to, w, next; }edges[maxn*2]; int head[maxn], cnt; int n, k; int vis[maxn], dis[maxn]; //顶点编号 set<int>st; void init() { memset(head, -1, sizeof(head)); cnt = 0; } inline void AddEdge(int a, int b, int id) { edges[id].to = b; edges[id].w = 1; edges[id].next = head[a]; head[a] = id; } int dis_v, max_dis; void dfs(int v, int dis) { //printf("%d %d\n", v, dis); vis[v] = 1; for(int i = head[v];i != -1;i = edges[i].next) { int u = edges[i].to; if(vis[u]) continue; if(st.find(u) != st.end()) { if(dis+1 > max_dis) { max_dis = dis + 1; dis_v = u; } } dfs(u, dis+1); } } int main() { init(); scanf("%d%d", &n, &k); for(int i = 0;i < n-1;i++) { int a, b; scanf("%d%d", &a, &b); AddEdge(a, b, ++cnt); AddEdge(b, a, ++cnt); } for(int i = 0;i < k;i++) { int tmp; scanf("%d", &tmp); st.insert(tmp); } dfs(*(st.begin()), 0); //printf("%d %d\n", dis_v, max_dis); memset(vis, 0, sizeof(vis)); max_dis = 0; dfs(dis_v, 0); //printf("%d %d\n", dis_v, max_dis); printf("%d\n", (max_dis+1)/ 2); return 0; }
个性签名:时间会解决一切