题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4607
题意:
给定一棵树,可以从树的任意一点开始访问树,问访问k个点最少经过边的次数
思路:
如果树中存在一条链大于等于k,那么只需要沿着这条链走下去不用走重复的边,故答案为k-1,而这条链也就是树的直径,如果树的直径小于k,那么要再访问树直径外的点,走过去再回来,于是额外的次数就是访问的树直径外的点数 * 2,则答案为len - 1 + (k - len) * 2,此处len为树直径上的点数
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
struct edge
{
int to, next;
}g[N*2];
bool vis[N];
int cnt, head[N];
int n, m, ans, dis[N];
void add_edge(int v, int u)
{
g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++;
}
int bfs(int s)
{
queue<int> que;
memset(dis, 0, sizeof dis);
memset(vis, 0, sizeof vis);
que.push(s), dis[s] = 1, vis[s] = true;
int ans = 0;
while(! que.empty())
{
int v = que.front(); que.pop();
for(int i = head[v]; i != -1; i = g[i].next)
{
int u = g[i].to;
if(! vis[u])
{
dis[u] = dis[v] + 1, vis[u] = true, que.push(u);
if(dis[u] > dis[ans]) ans = u;
}
}
}
return ans;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &m);
cnt = 0;
memset(head, -1, sizeof head);
for(int i = 1; i <= n - 1; i++)
{
int v, u;
scanf("%d%d", &v, &u);
add_edge(v, u), add_edge(u, v);
}
int ans = bfs(1);
ans = bfs(ans);
int len = dis[ans];
for(int i = 1; i <= m; i++)
{
int k;
scanf("%d", &k);
if(k <= len) printf("%d\n", k - 1);
else printf("%d\n", len - 1 + (k - len) * 2);
}
}
return 0;
}