题目描述

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. 【VS开发】【视频开发】利用ffmpeg+opencv实现画中画

    需求:把两路视频合成一路,即一个画面同时显示两路视频,其中一路缩小成小视频叠在大视频上面,和电视机的画中画效果类似. 思路:用h264编码的视频举例,文件中存储的es流是h264,经过解码成yuv,y ...

  2. python面试题100道

    python 100道面试题 1.一行代码实现1--100之和 利用sum()函数求和 2.如何在一个函数内部修改全局变量 函数内部global声明 修改全局变量 3.列出5个python标准库 os ...

  3. python文件夹操作

    1.遍历文件夹下所有文件2.将后缀为.DCM的文件复制到指定文件夹 import os import shutil def all_path(dirname): result = []#所有的文件 f ...

  4. seaborn做横向条形图

    数据格式如下 这里选择fold值最大的前20个作图进行展示 代码如下 import seaborn as sns import pandas as pd import numpy as np impo ...

  5. log sum of exponential

    The so-called “log sum of exponentials” is a functional form commonly encountered in dynamic discret ...

  6. win10无法安装软件解决

    https://www.windowscentral.com/how-fix-network-resource-unavailable-install-error-windows-10

  7. springmvc数据的封装

    spring封装是进行orm封装,可以进行定义数据类型,数据名与接收名相同,进行接收,或者定义类,类的属性名与接收名相同 单个数据类型如图下: 对象类型封装: 其他:乱码处理 在中文字符乱码,需要规定 ...

  8. mongdb基本使用

    mongodb创建用户,设置密码 参考:https://www.jianshu.com/p/237a0c5ad9fa MongoDB内置的数据库角色有: 1. 数据库用户角色:read.readWri ...

  9. 转 如何在调用WCF服务之前弹出一个确认对话框

    自定义InteractiveChannelInitializer(InvocationConfirmationInteractiveChannelInitializer)定义如下.我们在BeginDi ...

  10. 将ftp目录映射为本地盘符

    1.ftpuser 下载安装FTPUSE下载地址:http://www.ferrobackup.com/ftpuse/ 创建  FTPUSE F: www.xx.com pwd  /USER:admi ...