UVA1267 Network 题解(树上贪心)

题目传送门
水蓝题,实际难度绿。

题目分析

本题的难点在于如何确定加上去的服务器的位置。
假定以最初给定的服务器作为根节点。
对于任意两个未覆盖的叶子结点 \(A\),\(B\),设 \(A\) 的深度小于 \(B\),则对于 \(A\),我们可以往深度较浅或深度较深的方向放置服务器。若将服务器放置在深度较浅的位置,则 \(B\) 一定不能被覆盖,若将服务器放置在深度较深的位置,则 \(B\) 可能会被覆盖。
对于任意一个未覆盖的叶子结点,尽可能地将服务器放得远,即放置在距离该结点距离为 \(k\) 的位置,则服务器能覆盖的期望范围越大。
综上所述,我们得出一个贪心策略:选择深度最深的未覆盖的叶子结点,将服务器放置它的 \(k\) 级祖先处,置该处深度为 \(0\),由该处出发更新其他点的深度并使之最小,重复上述操作直至所有叶子结点均被覆盖。
详细细节见代码注释。

代码

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int MAXN=1000+114514;

int T, n, s, k;

vector<int> G[MAXN];
int in[MAXN], dist[MAXN], fa[MAXN];
int ans;

struct cmp{
	bool operator()(pii x, pii y){
		return x.first == y.first ? x.second < y.second : x.first < y.first;
	}
};
priority_queue<pii, vector<pii>, cmp> q; //使用堆储存待处理的叶子结点

int rd()
{
	int x = 0, sgn = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') sgn = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	return x * sgn;
}

void init()
{
	memset(in, 0, sizeof(in) );
	memset(dist, 0, sizeof(dist) );
	memset(fa, 0, sizeof(fa) );
	
	for(int i = 0; i < MAXN; ++i) 
		while(!G[i].empty() ) G[i].pop_back();
	
	while(!q.empty()) q.pop();
	
	ans = 0;
}

void get_dis(int s, int fath)
{
	for(int i = 0; i < G[s].size(); ++i)
	{
		int to = G[s][i];
		if(to == fath) continue;
		fa[to] = s;
		dist[to] = dist[s] + 1;
		get_dis(to, s);
	}
}

void update(int s, int fath)
{
	for(int i = 0; i < G[s].size(); ++i)
	{
		int to = G[s][i];
		if(to == fath) continue;
		if(dist[to] > dist[s] + 1) //更新深度,取该点到各个服务器距离的最小值
		{
			dist[to] = dist[s] + 1;
			update(to, s);
		}
	}
}

int main()
{
	T = rd();
	while(T--)
	{
		init();
		n = rd(); s = rd(); k = rd();
		for(int i = 1; i <= n - 1; ++i)
		{
			int x = rd(), y = rd();
			G[x].push_back(y);
			G[y].push_back(x);
			++in[x]; ++in[y];
		}
		get_dis(s, 0);
		for(int i = 1; i <= n; ++i)
			if(in[i] == 1 && dist[i] > k) q.push(make_pair(dist[i], i) ); //若度为1,则该点为叶子结点
		
		while(!q.empty() )
		{
			pii x = q.top(); q.pop();
			int dep = x.first, id = x.second, tmp = k;
			if(dist[id] <= k) continue; //如果更新深度后该点的深度小于等于k,则不需要处理
			while(tmp--) id = fa[id]; //寻找k级祖先
			dist[id] = 0;
			update(id, 0);
			++ans;
		}
		printf("%d\n", ans);
	}
	return 0;
}
上一篇:生成随机密码


下一篇:STL:标准异常类使用、编写自己的异常类