例题1.15 网络 UVALive 3902

1.题目描述:点击打开链接

2.解题思路:本题要求放置尽可能少的服务器,使得所有的客户端到最近的服务器的距离都不超过k。由于已经放置了一个服务器,不妨把它当做根结点,先把无根树转化为有根树,然后我们考虑最深的叶子,那么不难证明,该叶子结点的最优服务器的放置位置是它的k级祖先。这样本题的算法便不难想出:从最深的叶子开始枚举,并在它的k级祖先处放置一个服务器,同时标记该服务器能覆盖到的所有结点,这样当所有的叶子都被覆盖到时,服务器数量就是最少的。只用枚举到第k+1层叶子即可停止,因为小于等于k的叶子已经被根结点覆盖了。

本题有一个技巧:为了便于找到深度为k的所有叶子,我们在转化过程中顺便将是叶子的结点加入到nodes数组中,这样nodes[k]即表示了第k层的所有叶子。

3.代码:

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

#define N 1000+10
vector<int>gr[N], nodes[N];
int n, s, k, fa[N];
bool covered[N];
void dfs(int u, int f, int d)//无根树转有根树,计算fa数组,根据深度把叶子结点插入nodes表中
{
	fa[u] = f;
	int nc = gr[u].size();
	if (nc == 1 && d > k)nodes[d].push_back(u);//nc==1说明是叶子结点
	for (int i = 0; i < nc; i++)
	{
		int v = gr[u][i];
		if (v != f)dfs(v, u, d + 1);
	}
}
void dfs2(int u, int f, int d)
{
	covered[u] = true;
	int nc = gr[u].size();
	for (int i = 0; i < nc; i++)
	{
		int v = gr[u][i];
		if (v != f&&d < k)dfs2(v, u, d + 1);
	}
}
int solve()
{
	int ans = 0;
	memset(covered, 0, sizeof(covered));
	for (int d = n - 1; d>k; d--)//最大深度是n-1,从最大深度的叶子考虑
	for (int i = 0; i < nodes[d].size();i++)
	{
		int u = nodes[d][i];
		if (covered[u])continue;
		int v = u;
		for (int j = 0; j < k; j++)v = fa[v];//求u的k级祖先v
		dfs2(v, -1, 0);//在结点v放服务器,并覆盖相应的结点
		ans++;
	}
	return ans;
}
int main()
{
	//freopen("t.txt", "r", stdin);
	int T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d%d", &n, &s, &k);
		for (int i = 1; i <= n; i++)
		{
			gr[i].clear();
			nodes[i].clear();
		}
		for (int i = 0; i < n - 1; i++)
		{
			int a, b;
			scanf("%d%d", &a, &b);
			gr[a].push_back(b);
			gr[b].push_back(a);
		}
		dfs(s, -1, 0);
		printf("%d\n", solve());
	}
	return 0;
}


你可能感兴趣的:(DFS,无根树转化)