P2279 [HNOI2003]消防局的设立[树形dp]
题目描述
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]的更多相关文章
- 【BZOJ1217】[HNOI2003]消防局的设立 树形DP
[BZOJ1217][HNOI2003]消防局的设立 Description 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地, ...
- [HNOI2003]消防局的设立 树形dp // 贪心
https://www.luogu.org/problemnew/show/P2279 一开始就想到了贪心的方法,不过一直觉得不能证明. 贪心的考虑是在深度从深到浅遍历每个结点的过程中,对于每个没有覆 ...
- bzoj1217: [HNOI2003]消防局的设立 [树形dp]
Description 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了 ...
- luogu 2279 [HNOI2003]消防局的设立 树形dp
就是细节多一些,思路都非常常规. Code: #include <bits/stdc++.h> #define N 1005 #define inf 1061109567 #define ...
- [HNOI2003] 消防局的设立 - 树形dp
仍然是点覆盖集问题,但覆盖半径变成了\(2\) 延续上一题的思路,只是式子更加复杂了 想体验一下min_element大法于是不想优化了 #include <bits/stdc++.h> ...
- P2279 [HNOI2003]消防局的设立
P2279 [HNOI2003]消防局的设立考场上想出了贪心策略,但是处理细节时有点问题,gg了.从(当前深度最大的节点)叶子节点往上跳k个,在这里设消防局,并从消防局遍历k个距离,标记上. #inc ...
- [luogu]P2279 [HNOI2003]消防局的设立[贪心]
[luogu]P2279 [HNOI2003]消防局的设立 题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两 ...
- P2279 [HNOI2003]消防局的设立 贪心or树形dp
题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状 ...
- 洛谷 P2279 [HNOI2003]消防局的设立 (树形dp or 贪心)
一看到这道题就知道是树形dp 之前做过类似的题,只不过保护的范围是1 所以简单很多. 这道题保护的范围是2,就复杂了很多. 我就开始列状态,然后发现竟然有5种 然后我就开始列方程. 但是我考虑的时候是 ...
随机推荐
- SpringBoot系列教程web篇之Thymeleaf环境搭建
上一篇博文介绍了如何使用Freemaker引擎搭建web项目,这一篇我们则看一下另外一个常见的页面渲染引擎Thymeleaf如何搭建一个web项目 推荐结合Freemaker博文一起查看,效果更佳 1 ...
- LeetCode 503. 下一个更大元素 II(Next Greater Element II)
503. 下一个更大元素 II 503. Next Greater Element II 题目描述 给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素.数字 ...
- [转帖]腾讯将使用AMD第二代霄龙处理器打造自研服务器:性能提升35%
腾讯将使用AMD第二代霄龙处理器打造自研服务器:性能提升35% https://news.cnblogs.com/n/647499/ 我司的服务器是不是要少一块蛋糕了.. 作者:万南 今日,AMD 宣 ...
- [转帖]WannaCry惊天大发现!疑似朝鲜黑客组织Lazarus所为
WannaCry惊天大发现!疑似朝鲜黑客组织Lazarus所为 Threatbook2017-05-16共588524人围观 ,发现 17 个不明物体系统安全 https://www.freebuf. ...
- c++基础(七)——面向对象程序设计
面向对象程序设计(Object-oriented programming)的核心思想是数据抽象,继承,和动态绑定. 1. 继承 在C++语言中,基类将类型相关的函数与派生类不做改变直接继承的函数区分对 ...
- flask框架(二)——flask4剑客、flask配置文件的4种方式
之前学习的Django有必备三板斧:render,HttpResponse,redirect,JsonResponse 在flask也有,但是有些不同 一.Flask4剑客 1.直接返回字符串(ret ...
- C++学习笔记 之 循环
循环 循环语句允许我们多次执行一个语句或者语句组.(插入流程图) 循环类型 C++为我们提供的循环类型如下: 循环类型 描述 while循环 当给定条件为真时,重复语句或语句组.它会在执行循环主体之前 ...
- MySQL数据库-表操作-SQL语句(一)
1. 数据库操作与存储引擎 1.1 数据库和数据库对象 数据库对象:存储,管理和使用数据的不同结构形式,如:表.视图.存储过程.函数.触发器.事件等. 数据库:存储数据库对象的容器. 数据库分两种 ...
- 2. 执行Spark SQL查询
2.1 命令行查询流程 打开Spark shell 例子:查询大于21岁的用户 创建如下JSON文件,注意JSON的格式: {"name":"Michael"} ...
- Jenkins + GitLab + SpringBoot 实现持续集成脚本
Linux脚本 #!/bin/bash jar_name=hq-api.jar cd /usr/local/app/hq-api echo "Stopping SpringBoot Appl ...