BZOJ1217 || 洛谷P2279 [HNOI2003]消防局的设立【树形D(tan)P(xin)】

Description

2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地。起初为了节约材料,人类只修建了n-1条道路来
连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地A到
基地B至少要经过d条道路的话,我们称基地A到基地B的距离为d。由于火星上非常干燥,经常引发火灾,人类决定
在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过2的基地的火灾。
你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾

Input

第一行为n,表示火星上基地的数目。N<=1000
接下来的n-1行每行有一个正整数,其中文件第i行的正整数为a[i],表示从编号为i的基地到编号为a[i]的基地之间有一条道路,
为了更加简洁的描述树状结构的基地群,有a[i] < i

Output

仅有一个正整数,表示至少要设立多少个消防局才有能力及时扑灭任何基地发生的火灾。


题目分析

这样的题型完全可以扩展出
可覆盖距离为任意数值k的求解模型

其实思路有点贪心的感觉
d p [ u ] dp[u] dp[u]记录当前离u最近的消防局的距离
先对整棵树dfs,将节点按深度降序排列
然后从深度最大的结点开始依次遍历

若当前结点u未被控制,则 a n s + + ans++ ans++
找到u的第k级祖宗gra (若不足k代就选择根结点)
然后从gra开始向外扩展更新与其距离不超过k的结点的dp值

整个算法复杂度 O ( n ∗ k ) O(n*k) O(nk),绰绰有余

另外的两道相同模型,三倍经验
洛谷P3942 将军令
洛谷P2899 [USACO08JAN]Cell Phone Network


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
 
int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}
 
const int maxn=200010;
int n,k=2;
struct edge{int v,nxt;}E[maxn<<1];
int head[maxn],tot;
struct node{int u,dep;}pt[maxn];
int dp[maxn],fa[maxn],ans;
bool cmp(node a,node b){return a.dep>b.dep;}
 
void add(int u,int v)
{
    E[++tot].nxt=head[u];
    E[tot].v=v;
    head[u]=tot;
}
 
void dfs(int u,int pa)
{
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(v==pa) continue;
        fa[v]=u; pt[v].u=v; 
        pt[v].dep=pt[u].dep+1;
        dfs(v,u);
    }
}
 
void update(int u)
{
    if(!dp[u])return;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(dp[v]<dp[u]-1)
        dp[v]=dp[u]-1,update(v);
    }
}
 
int main()
{
    n=read();
    for(int i=2;i<=n;++i)
    {
        int u=read();
        add(i,u); add(u,i);
    }
     
    fa[1]=pt[1].u=pt[1].dep=1;
    dfs(1,1);
     
    sort(pt+1,pt+1+n,cmp);//按深度降序排列
    memset(dp,-1,sizeof(dp));
    for(int i=1;i<=n;++i)
    {
        int u=pt[i].u;
        if(dp[u]==-1)//发现未被控制的结点
        {
            ans++;
            for(int j=k;j>=1;--j)u=fa[u];//找到第k级祖宗
            dp[u]=k; update(u);//扩展更新dp值
        }
    }
    printf("%d",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值