消防局的设立

题目地址

一道可以用贪心做的树形DP题。

对于深度最深的点,要使它被覆盖到,可以在它的兄弟、父亲、爷爷处设立消防局,而在它的爷爷设置消防局最优因为这样可以管到爷爷的爷爷

每设立一个消防局,又要把它能管到的地方都标记一遍。所以要打两个标记。一个标记记录它是否被任何一个消防局管到过\(vis\)。一个标记\(book\)记录它能否被当前设立的这一个消防局管到,这个标记要在设立下一个消防局时清零。
原因:\(book\)是防止父亲和儿子之间来回搜(加的双向边嘛)。如果遇到一个点的\(book\)为1,就会\(continue\),但如果这个\(book\)是被之前设立的消防局标记的,就会出现错误。

举个例子:
enter image description here

假如第一次取出最深的点1,相当于在右边的蓝色点设立消防局,那么所有红色的点的\(book\)都被标记过了。如果不清0的话,第二次选到2号点,相当于在左边的蓝色点设立消防局,它应该将所有标箭头的点都标记,但以为上一次标记的\(book\)没有清空,当搜到最上面的红色的点时,就会返回,所以橙色的点并没有被标记到(它应该被标记)。那么下次又会取出橙色的点。很明显答案错误。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 10005;
int n,head[N],tot,ans;
struct edge{
    int next,node;
}e[20005];
struct nod{
    int dep,p;
}a[N];
bool vis[N],book[N];
void add(int x,int y)
{
    e[++tot].node=y;
    e[tot].next =head[x];
    head[x]=tot;
}
bool cmp(nod a,nod b)
{
    return a.dep >b.dep;
}
void build(int u,int fa)
{
    a[u].p=u; a[u].dep=a[fa].dep+1;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].node;
        if(v==fa) continue;
        build(v,u);
    }
}
void dfs(int u,int sum)
{
    book[u]=vis[u]=1;
    if(!sum) { book[u]=0; return;}
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].node;
        if(book[v]) continue; 
        dfs(v,sum-1);
    }
    book[u]=0;
}
/*void dfs(int x,int size)
{
    vis[x]=book[x]=1;
    for(int i=head[x];i;i=e[i].next)
    {
        int to=e[i].node;
        if(!book[to]&&size>0)
         dfs(to,size-1); 
    }
    book[x]=0;
}*/
int main()
{
    scanf("%d",&n);
    int x;
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&x);
        add(i,x); add(x,i);
    }
    build(1,0);
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
    if(!vis[a[i].p])
     ++ans,dfs(a[i].p,4);
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/karryW/p/11478624.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值