https://www.luogu.org/problemnew/show/P2279
和刘汝佳白书第一章一道例题差不多。
确实是无根树,但是我们可以把它"看做"有根树,这样并不会影响这棵树的性质,这棵树该是什么样还是什么样的,这只是一个看待的方式,这样我们更容易去思考。
考虑当前深度最大的结点,肯定要有一个消防局去覆盖它,这个消防局可以是三个位置:他的父亲,他的兄弟,他的爷爷。
显然,他的父亲和兄弟可以覆盖到的点全部可以被他的爷爷覆盖到,并且他的爷爷可能覆盖到更多的结点,因此选他的爷爷做消防局。
那么,做法就有了:先预处理出各个深度的点集,然后从深到浅,遇到未覆盖结点时取它的爷爷做消防局,并以它的爷爷为起点dfs2层,标记掉覆盖的结点。
这样的复杂度比n^2要小,但比n要大得多,我是不会算。
洛谷排名第一的题解给了一种更好的实现https://www.luogu.org/blog/contributation/solution-p2279 ,非常简短。
这是我的代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000+100
int n,coverd[maxn],fa[maxn],ans;
vector<int> G[maxn],nodes[maxn];
void dfs(int u,int f,int d)
{
fa[u]=f;
nodes[d].push_back(u);
for(int i=0;i<G[u].size();i++)if(G[u][i]!=f)
dfs(G[u][i],u,d+1);
}
void dfs2(int u,int f,int d)
{
coverd[u]=1;
for(int i=0;i<G[u].size();i++)if(G[u][i]!=f&&d<=1)
dfs2(G[u][i],u,d+1);
}
void solve()
{
for(int i=n-1;i>=0;i--)
{
for(int j=0;j<nodes[i].size();j++)
{
int u=nodes[i][j];
if(coverd[u])continue;
if(fa[u]!=-1)u=fa[u];
if(fa[u]!=-1)u=fa[u];
ans++;
dfs2(u,-1,0);
}
}
}
int main()
{
freopen("input.in","r",stdin);
int x;
cin>>n;
for(int i=2;i<=n;i++)
{
cin>>x;
G[i].push_back(x);
G[x].push_back(i);
}
dfs(1,-1,0);
solve();
cout<<ans<<endl;
return 0;
}
这是那位大佬的代码我小改一点的:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 1020
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int n,b[N],f[N],d[N],o[N],ans,u,v,w;
bool cmp(int x,int y){return d[x]>d[y];}
int main(){
scanf("%d",&n);b[1]=1,o[1]=o[0]=N;
FOR(i,2,n) scanf("%d",&f[i]),d[i]=d[f[i]]+1,b[i]=i,o[i]=N;
sort(b+1,b+n+1,cmp);
FOR(i,1,n){
v=b[i],w=f[v],u=f[f[v]];
o[v]=min(o[v],min(o[w]+1,o[u]+2));
if(o[v]>2){
o[u]=0,ans++;
o[f[u]]=1,o[f[f[u]]]=2;
}
}printf("%d",ans);
}