洛谷P2279-HNOI2003 消防局的设立

博客详细讨论了洛谷P2279题——HNOI2003消防局的设立问题。文章指出,尽管题目涉及的是无根树,但可以将其视为有根树来简化思考。解决策略是从最大深度节点开始,选择其祖父作为消防局,以减少覆盖范围。通过预处理节点集合并从深到浅遍历,博主分享了自己的代码实现,并引用了洛谷排名第一的简洁解法链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值