题目大意:
给定一棵n个结点的树,从中选出一个最小点集S,使得树中每一个点到点集的最小距离≤k。
分析:
根据贪心原则,每两个S中的点,尽量使得两个点之间的距离=2k+1,因为如果两个点距离≤2k+1,中间所有的点都是可以覆盖得到的。
设fi表示i到子树中某一个S中的点的距离。
如果i为叶子结点,fi=k+1,表示它只能由kth ancestor覆盖(看不懂请往下);
如果i有一个孩子j,那么fi=fj+1;
如果i有两个以上的孩子,那么从孩子中选一个最大的fmax,和最小的fmin。如果fmin+fmax≤2k+1,表示这棵子树中所有的点都可以被覆盖,fi=fmin+1,没必要再选根结点;如果fmin+fmax>2k+1,表示这棵子树中所有的点没有都被覆盖,那么这时fi=fmax+1。如果fi=2k+1,那么将这个点加入S中,fi置为0。
对于根节点,如果f1>k那么也要将其加入S中。
AC code:
#include <cstdio>
#include <vector>
#include <algorithm>
#define pb push_back
using namespace std;
const int MAXN = 30009;
const int INF = 0x3f3f3f3f;
int n, k;
int rank[MAXN];
bool is[MAXN];
vector<int> ans;
struct Ugraph
{
int size;
int head[MAXN];
int to[MAXN<<1];
int ne[MAXN<<1];
Ugraph() {size = 1;}
void add_edge(int u, int v)
{
to[size] = v, ne[size] = head[u], head[u] = size++;
to[size] = u, ne[size] = head[v], head[v] = size++;
}
}G;
void dfs(int x, int fa)
{
int y, minr = INF, maxr = -INF, c = 0;
for(int i = G.head[x]; i; i = G.ne[i])
{
y = G.to[i];
if(y != fa)
{
dfs(y, x);
minr = min(minr, rank[y]);
maxr = max(maxr, rank[y]);
c++;
}
}
if(!c) rank[x] = k+1;
else if(c == 1) rank[x] = minr+1;
else
{
if(minr+maxr+2 <= 2*k+1) rank[x] = minr+1;
else rank[x] = maxr+1;
}
if(rank[x] == 2*k+1) is[x] = true, rank[x] = 0;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
scanf("%d%d", &n, &k);
for(int i = 1; i < n; ++i)
{
int u, v;
scanf("%d%d", &u, &v);
G.add_edge(u, v);
}
dfs(1, 0);
for(int i = 1; i <= n; ++i)
if(is[i] || (i == 1 && rank[i] > k))
ans.pb(i);
printf("%d\n", ans.size());
for(int i = 0, sz = ans.size(); i < sz; ++i)
printf("%d\n", ans[i]);
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
#endif
return 0;
}
时间: 2024-10-13 13:10:59