P2279 [HNOI2003]消防局的设立

//P2279 [HNOI2003]消防局的设立
//http://blog.csdn.net/kenxhe/article/details/53026536写得不错
//http://blog.csdn.net/qwsin/article/details/50954698写得也可以
//树形动规的写法,没人指导,确实看不懂,无奈,找能看懂得来研究。
//http://www.cnblogs.com/JSZX11556/p/5102075.html此文做法,摘抄如下:
//一个简单的贪心, 我们只要考虑2个消防局设立的距离为5时是最好的, 因为利用最充分. 就dfs一遍, 再对根处理一下就可以了. 这道题应该是SGU某道题的简化版...这道题距离只有2, 树型dp应该也是可以的
//http://blog.csdn.net/LOI_QER/article/details/52650170思路,摘抄如下:
//思路:考虑到 一个消防局可以扑灭与他距离 <= 2 的所有点。那么我们每隔4个点放一个消防局是最优的。
//在 dfs 中:我们用数组 f[] 标记每个点 某种程度上可以说 abs(f[x]-5) 就是 x 到消防局的距离。当 f[x] == 5 时 那我们另 f[x] == 0 即,把这个看为一个消防局 ans ++;
//还有一个地方就是处理一下边界,即开始的点,如果走到了这里而这里距离最近的消防局的距离大于等于3,也就是这里没有被消防局覆盖,那就只能在这里也加上一个消防局了。
//http://blog.csdn.net/zhouyuyang233/article/details/69941733代码写得不错
//http://blog.csdn.net/pbihao/article/details/52728013注释写得不错
//很无奈,最容易理解的,代码比较长,程序还是按好理解的来写,http://blog.csdn.net/qq_33728185/article/details/52496878摘抄如下:
//首先,该题树形DP可解,但是这里介绍贪心的做法。
//显然,对于目前深度最深的点,如果他的父亲、爷爷、他父亲的儿子和它自己上没有建消防局,那么该点不可能被覆盖。而在这些点中,讲消防局建在爷爷节点收益最大,因为爷爷节点建消防局覆盖的范围一定包括父亲结点与该节点发亲的儿子建消防局的范围。

//这样,我们按照深度把所有结点排一下序,然后每次重复在最深的节点的爷爷节点上建立消防局即可。

//为了更好的理解该题程序,编写了几组样例数据,并配图

//样例1:

配图:


输入:

6
1
2
3
4
5

输出:

2

//样例2

//配图:


//输入:

4

1

1

1


//输出:

1

//样例3:

//配图:


//输入:

16
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8


//输出:

2

//编写测试数据过程中,深深的感觉到,该题的最小深度是以节点1为根节点的树。

//编写程序一蹴而就,2017-8-22 21:30 AC
//树,图,(深度优先遍历)是少不了的。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int head[1000+10],cnt=0,father[1000+10],vis[1000+10],ans=0;//father[x]节点x的父节点
struct node1{
    int d,v;//d深度 v节点值
}q[1000+10];
struct node2{
    int to,next;//to指向节点,next下一条边
}e[1000*2+10];//正向边,反向边
void addedge(int u,int v){//邻接表 存储方式
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
int cmp(node1 a,node1 b){//由大到小排序
    return a.d>b.d;
}
void deep(int x,int f){//获取节点深度 u是子节点,f是父节点
    int b,v;
    father[x]=f;
    q[x].d=q[f].d+1;//深度计算 父节点深度+1
    q[x].v=x;
    for(b=head[x];b;b=e[b].next){
        v=e[b].to;
        if(f==v)continue;//f深度以及值已被设置过,无需设置,故continue
        deep(v,x);
    }
}
void station(int x,int c){//标记能被扑灭的节点,c访问层数
    int b;
    if(c>2)return;
    vis[x]=1;
    for(b=head[x];b;b=e[b].next)
        station(e[b].to,c+1);
}
int main(){
    int n,i,j,v;
    memset(head,0,sizeof(head));
    memset(father,0,sizeof(father));
    memset(vis,0,sizeof(vis));
    q[0].d=0;
    scanf("%d",&n);
    for(i=2;i<=n;i++){
        scanf("%d",&v);
        addedge(i,v),addedge(v,i);//邻接表,正向边,反向边
    }
    deep(1,0);
    sort(q+1,q+n+1,cmp);//按深度,由大到小进行排序
    for(i=1;i<=n;i++){
        if(vis[q[i].v])continue;
        if(father[father[q[i].v]])v=father[father[q[i].v]];
        else if(father[q[i].v])v=father[q[i].v];
        else v=q[i].v;
        station(v,0);//v是设置消防局的节点
        ans++;
    }
    printf("%d\n",ans);
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值