题目大意:有n个基地,n-1条边,且没两个基地可达。从基地A到基地B至少要经过d条边,则两基地的距离为d。现在要在基地上建消防站,且每个消防站有能力扑灭与它距离不超过2的基地的火灾。问至少要建多少个消防站才能确保所有基地都发生火灾时,能扑灭全部的火
(1)将无根树转化为有根树
(2)找到未被标记且深度最深的点pos
(3)若有符合要求的pos, 就建立一个消防站,然后从点x染色
若pos的深度是1,即为root,则x = pos;
若pos的深度是2,则没有爷节点,那么x = fa[pos];
若pos的深度>2,则有爷节点,那么x = fa[fa[pos]];
(4)重复(2)(3)直到没有符合要求的pos
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1010;
int n, ans;
int to[maxn<<1], next[maxn<<1];//链式前向星
int head[maxn], tot;
int dep[maxn];//深度,用于无根树转化为有根树
int fa[maxn];//father, 初始化为0
int vis[maxn];//标记该点是否染色
void addedge(int u, int v)
{
++tot;
to[tot] = v;
next[tot] = head[u];
head[u] = tot;
}
void dfs(int u, int f)//u的fa是f
{
dep[u] = dep[f] + 1;
fa[u] = f;
for(int i = head[u]; i; i = next[i])
if(to[i] != f)
dfs(to[i], u);
}
void col(int u, int f, int dis)//记录fa:f避免重复染色
{
if(dis == 3) return;
vis[u] = 1;
for(int i = head[u]; i; i = next[i])
if(to[i] != f)
col(to[i], u, dis+1);
}
int main()
{
int i, x;
scanf("%d", &n);
memset(fa, 0, sizeof(fa));
memset(vis, 0, sizeof(vis));
memset(head, 0, sizeof(head));
ans = 0;
tot = 0;
for(i = 2; i <= n; ++i)
{
scanf("%d", &x);
addedge(i, x);
addedge(x, i);
}
dfs(1, 0);//无根树转有根树
while(1)
{
int pos = 0, maxdep = 0;
for(i = 1; i <= n; ++i)
if(!vis[i] && dep[i] > maxdep)
maxdep = dep[i], pos = i;
if(!pos) break;
++ans;//建立一个消防站
//染色
if(dep[pos] == 1) col(pos, 0, 0);
else if(dep[pos] == 2) col(fa[pos], 0, 0);
else col(fa[fa[pos]], 0, 0);
}
printf("%d\n", ans);
return 0;
}