题意:给出小于等于1000个结点的树,叶子结点为客户端,其他结点为服务器。给出点S,代表只有服务器S安装了某项服务,给出距离K,代表每个客户端在不超过K的距离范围内必须有一个安装了该服务的服务器。问至少还有多少服务器需要安装该服务?
解法:之前想的是DP,看了解法感觉想麻烦了,因为了没有分析性质。不过有一点想对了,因为子树之间互相关联,不能考虑某个服务器安装或者不安装服务。考虑的落脚点是每个客户端依赖于哪个服务器。
将无根树转化为以S为根的有根树,对于每个叶子结点,当然是选择越靠近根结点的服务器作为其最近依赖点最好(贪心)。
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
typedef long long ll;
typedef pair<int, int> pii;
const int INF =0x3f3f3f3f;
const int maxn= 1000 ;
int n,S,K;
vector<int >G[maxn+10],nodes[maxn+10];
int pre[maxn+10];
bool covered[maxn+10];
void init()
{
memset(covered,0,(n+1)*sizeof covered[0]);
for1(i,n) G[i].clear(),nodes[i].clear();
}
void dfs(int x,int fa,int d)
{
if(fa!=-1) pre[x]=fa;
int siz=G[x].size();
if(siz==1)
{
nodes[d].push_back(x);
if(d<=K) covered[x]=1;
}
for(int i=0;i<siz;i++)
{
int y=G[x][i];
if(y==fa) continue;
dfs(y,x,d+1);
}
}
void dfs2(int x,int fa,int d)
{
if(d<=K) covered[x]=1;
int siz=G[x].size();
for0(i,siz)
{
int y=G[x][i];
if(y==fa) continue;
if(d<K) dfs2(y,x,d+1);
}
}
int solve()
{
int ans=0;
for(int d=n-1;d>K;d--)
{
int siz=nodes[d].size();
for0(i,siz)
{
int y=nodes[d][i];
if(covered[y]) continue;
int t=y;
for1(i,K) t=pre[t];//覆盖距离为K,之前写成了K-1
dfs2(t,-1,0);
ans++;
}
}
return ans;
}
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&S,&K);
int x,y;
init();
for1(i,n-1)
{
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
pre[S]=S;
dfs(S,-1,0);
printf("%d\n",solve() );
}
return 0;
}