题目链接:https://cn.vjudge.net/problem/UVALive-3902
题意:(蓝书)
思路:(把图当作以起始服务器为根的树)
要想满足最少这个条件,就必须尽可能的让叶结点距离服务器远一点,也就是距离正好是 k,这样能够减少很多不必要的服务器;所以可以先把叶结点找出来并且存起来,依次遍历叶结点,以叶结点为准,找一个距离为k的点作为服务器,在把这个服务器k范围内的点都标记为覆盖,覆盖的点就不用考虑了;
为什么一定要从最底层的叶结点开始找服务器?
因为顶部的叶结点可以被下层的服务器覆盖,如果你从顶部的开始找,那么dfs找到的这个服务器可能会在这个叶结点的上层,自然就不会是最优解,但是如果是从底层开始,那么只可能找到比底层叶结点高的点作为服务器,这样就能保证最优解了;
这里要注意的是,遍历的点都是k+1层到n-1的点,因为我们根本不用考虑k层内的点,起始点就已经覆盖k层内的点了;下面的代码基本上和蓝书的一样,有很多值得学习的地方,我都用注释标记出来了;
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<sstream>
#include<vector>
#include<string>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0);
#define REP(i,n) for(int i=0;i<n;++i)
int read(){
int r=0,f=1;char p=getchar();
while(p>'9'||p<'0'){if(p=='-')f=-1;p=getchar();}
while(p>='0'&&p<='9'){r=r*10+p-48;p=getchar();}return r*f;
}
typedef long long ll;
typedef unsigned long long ull;
const int Maxn = 1000+10;
const long long LINF = 1e18;
const int INF = 0x3f3f3f3f;
const int Mod = 10001;
const double PI = acos(-1.0);
vector<int> edges[Maxn],nodes[Maxn];
int fa[Maxn],k,n,s;
bool used[Maxn];
void dfs (int u,int f,int d) { // 把所有 深度大于k的叶结点存入nodes中,并且完成fa[]的赋值
fa[u] = f; // fa[]能存储所有点的父节点,树的每一个节点只有一个前继
int m = edges[u].size();
if(m == 1 && d > k) nodes[d].push_back(u); // 这里的nodes是用来存储第d层的所有叶节点
for (int i = 0; i < m; ++i) {
int v = edges[u][i];
if(v != f) dfs (v,u,d+1); // 这里的f是为了避免重复走之前走过的路
}
}
void dfs2 (int u,int f,int d) {
used[u] = true;
int m = edges[u].size();
for (int i = 0; i < m; ++i) {
int v = edges[u][i];
if(v != f && d < k) dfs2 (v,u,d+1); // f的作用就是为了避免重复走之前走过的路,
} //不用多开一个vis的数组;d < k : 把从服务器开始的
} //的k范围内的点全部覆盖
int solve () {
memset(used,0,sizeof(used));
int v,ans = 0;
for (int d = n-1; d > k; --d) { // 深度从k+1到n-1
for (int j = 0; j < nodes[d].size(); ++j) {
v = nodes[d][j];
if(used[v]) continue; // 处理过的点就直接跳过
for (int i = 0; i < k; ++i) v = fa[v]; // 从叶结点开始的第k个中间节点作为服务器
dfs2 (v,-1,0); //用fa不断的向上找前继(fa在找叶结点的时候就顺便完成赋值了)
ans++;
}
}
return ans;
}
int main (void)
{
int t;
scanf("%d",&t);
while (t--) {
scanf("%d%d%d",&n,&s,&k);
for (int i = 0; i <= n; ++i) { edges[i].clear(); nodes[i].clear(); }
int u,v;
for (int i = 1; i < n; ++i) { // n个点,n-1条边,一棵树
scanf("%d%d",&u,&v);
edges[u].push_back(v);
edges[v].push_back(u);
}
dfs (s,-1,0);
printf("%d\n",solve ());
}
return 0;
}