题目描述

2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地。起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地A到基地B至少要经过d条道路的话,我们称基地A到基地B的距离为d。

由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过2的基地的火灾。

你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。

解析

这道题似乎可以贪心,但是窝是为了dp而来的,显然这是个树形dp。

定义状态:

\(dp[0][x]\)表示消防局已经覆盖了以\(x\)为根节点的子树和该节点的父节点和爷爷节点,可以看作该点有消防局;

\(dp[1][x]\)表示已经至少覆盖了以\(x\)为根节点的子树和该节点的父节点,可以看作该点的儿子有消防局;

\(dp[2][x]\)表示已经至少覆盖的以\(x\)为根节点的子树,可以看作该点的孙子节点有消防局;

\(dp[3][x]\)表示已经至少覆盖了所有以\(x\)的儿子为根节点的子树;

\(dp[4][x]\)表示已经至少覆盖了所有以\(x\)的孙子为根节点的子树,后面这俩都是用来给上面状态的转移作中介的。

务必明确一点:实际上对于某个状态,我们刻画其为以某点为根的子树子树与父节点、爷爷节点都被覆盖,并且子树包含根节点。

对于状态\(dp[0][x]\),显然在\(x\)节点就必须有一个消防局,才能满足状态定义。假设其儿子节点的集合为\(t\),且有\(y\in t\),为了使得答案最优,我们直接从\(dp[4][y]\)转移过来。

也就是:\(dp[0][x]=1+\sum_{y \in t}dp[4][y]\)

对于状态\(dp[1][x]\),显然在\(x\)的某个儿子节点上必须要有一个消防局,即\(dp[0][y]\),此时其它儿子节点都没有被覆盖时最优的,即\(dp[3][y]\)。由于状态\(dp[0][x]\)包含了这个状态,于是我们把它也加进来取\(\min\)。

沿用上面的假设,我们得到:\(dp[1][x]=\min\limits_{y\in t,z\in t且z\ne y}\{dp[0][y]+\sum dp[3][z],dp[0][x]\}\)

对于状态\(dp[2][x]\),与二相似,在\(x\)的某个孙子节点上必须要有一个消防局,即\(dp[1][y]\),此时其它儿子节点都恰好为状态\(dp[2][y]\)时为最优解。由于状态\(dp[0/1][x]\)都包含了这个状态,于是我们把它们也加进来取\(\min\)。

沿用上面的假设,我们得到:\(dp[2][x]=\min\limits_{y\in t,z\in t且z\ne y}\{dp[1][y]+\sum{dp[2][z]},dp[0/1][x]\}\)

对于状态\(dp[3][x]\),我们直接像\(dp[0][x]\)一样,加上所有\(dp[2][y]\)就行。然后类比上面三条,同理,\(dp[0/1/2][x]\)显然包含该状态。

沿用上面的假设,我们得到:\(dp[3][x]=\min\limits_{y\in t}\{\sum dp[2][y],dp[0/1/2][x]\}\)

同上。

注意:叶子节点和叶子节点的父节点,它们的\(dp[1][x],dp[2][x]\)状态必须有一个消防局,否则不满足状态定义,因此初始化的时候要稍微注意一下。


这道题就变得简单了。

参考代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define N 1010
#define INF 0x7fffffff
using namespace std;
struct node{
int next,ver;
}g[N<<1];
int tot,head[N],n;
int dp[5][N];
inline void add(int x,int y)
{
g[++tot].ver=y;
g[tot].next=head[x],head[x]=tot;
}
inline void dfs(int x)
{
dp[0][x]=1;dp[3][x]=0;dp[4][x]=0;
for(int i=head[x];i;i=g[i].next){
int y=g[i].ver;
dfs(y);
dp[0][x]+=dp[4][y];
dp[3][x]+=dp[2][y];
dp[4][x]+=dp[3][y];
}
if(head[x]==0){
dp[1][x]=dp[2][x]=1;
}
else{
dp[1][x]=dp[2][x]=INF;
for(int i=head[x];i;i=g[i].next){
int y=g[i].ver;
int t1=dp[0][y],t2=dp[1][y];
for(int j=head[x];j;j=g[j].next){
if(i==j) continue;
int z=g[j].ver;
t1+=dp[3][z];
t2+=dp[2][z];
}
dp[1][x]=min(dp[1][x],t1);
dp[2][x]=min(dp[2][x],t2);
}
}
for(int i=1;i<=4;++i)
dp[i][x]=min(dp[i][x],dp[i-1][x]);
}
int main()
{
scanf("%d",&n);
for(int i=2;i<=n;++i){
int u;
scanf("%d",&u);
add(u,i);
}
dfs(1);
cout<<dp[2][1]<<endl;
return 0;
}

P2279 [HNOI2003]消防局的设立[树形dp]的更多相关文章

  1. 【BZOJ1217】[HNOI2003]消防局的设立 树形DP

    [BZOJ1217][HNOI2003]消防局的设立 Description 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地, ...

  2. [HNOI2003]消防局的设立 树形dp // 贪心

    https://www.luogu.org/problemnew/show/P2279 一开始就想到了贪心的方法,不过一直觉得不能证明. 贪心的考虑是在深度从深到浅遍历每个结点的过程中,对于每个没有覆 ...

  3. bzoj1217: [HNOI2003]消防局的设立 [树形dp]

    Description 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了 ...

  4. luogu 2279 [HNOI2003]消防局的设立 树形dp

    就是细节多一些,思路都非常常规. Code: #include <bits/stdc++.h> #define N 1005 #define inf 1061109567 #define ...

  5. [HNOI2003] 消防局的设立 - 树形dp

    仍然是点覆盖集问题,但覆盖半径变成了\(2\) 延续上一题的思路,只是式子更加复杂了 想体验一下min_element大法于是不想优化了 #include <bits/stdc++.h> ...

  6. P2279 [HNOI2003]消防局的设立

    P2279 [HNOI2003]消防局的设立考场上想出了贪心策略,但是处理细节时有点问题,gg了.从(当前深度最大的节点)叶子节点往上跳k个,在这里设消防局,并从消防局遍历k个距离,标记上. #inc ...

  7. [luogu]P2279 [HNOI2003]消防局的设立[贪心]

    [luogu]P2279 [HNOI2003]消防局的设立 题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两 ...

  8. P2279 [HNOI2003]消防局的设立 贪心or树形dp

    题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状 ...

  9. 洛谷 P2279 [HNOI2003]消防局的设立 (树形dp or 贪心)

    一看到这道题就知道是树形dp 之前做过类似的题,只不过保护的范围是1 所以简单很多. 这道题保护的范围是2,就复杂了很多. 我就开始列状态,然后发现竟然有5种 然后我就开始列方程. 但是我考虑的时候是 ...

随机推荐

  1. 使用 Node.js 写一个代码生成器

    背景 第一次接触代码生成器用的是动软代码生成器,数据库设计好之后,一键生成后端 curd代码.之后也用过 CodeSmith , T4.目前市面上也有很多优秀的代码生成器,而且大部分都提供可视化界面操 ...

  2. Zookeeper架构及FastLeaderElection机制

    原文链接:http://www.jasongj.com/zookeeper/fastleaderelection/ Zookeeper是什么 Zookeeper是一个分布式协调服务,可用于服务发现,分 ...

  3. [Oracle] - 查看数据库中每个表占用空间大小,及进行表压缩

    查询用户创建的表 select * from user_tab_comments; -- 查询本用户的表,视图等. select * from user_col_comments; -- 查询本用户的 ...

  4. 【C++札记】多态

    C++中多态是面向对象设计思想的重要特性,同名具有不同功能函数,该函数调用过程执行不同的功能.多态的原理是通过一张虚函数表(Virtual Table)实现的.动多态会牺牲一些空间和效率来最终实现动态 ...

  5. linux shell程序常用功能

    一.循环读取文件 循环读取文件方式有多种,推荐下列方法 while read line;do local include=$(echo ${line} | grep "filter" ...

  6. 额。。。c++ sort()排序问题

    首先呢 记得 这是个快排 不稳定 基本格式 头文件 #include<algorithm> #include<iostream> bool cmp(int x,int y) { ...

  7. 接口标记为@ResponseBody却不进入ResponseBodyAdvice

    一.背景: 我们的接口为了统一,在ResponseBodyAdvice中对返回值做统一处理,默认添加了errorNo和errorInfo字段返回. 最近同事改接口代码的时候,发现接口返回值是空的.乍一 ...

  8. python之(TensorFlow)深度学习

    一.深度学习(DL, Deep Learning)是机器学习(ML, Machine Learning)领域中一个新的研究方向,它被引入机器学习使其更接近于最初的目标——人工智能(AI, Artifi ...

  9. Process.Start cmd 参数空格问题解决

    Process.Start("cmd.exe", "/c start  \"title\"  \"C:\\Program Files\\a. ...

  10. 2 Match、Filter、排序、分页、全文检索、短语匹配、关键词高亮

    查索引内所有文档记录 GET  /beauties/my/_search   GET  /beauties/my/_search {     "query":{         & ...