传送门啦

一个很摸不清头脑的树形dp

状态:

$ dp[i][0] $ :选自己

$ dp[i][1] $ :选了至少一个儿子

$ dp[i][2] $ :选了至少一个孙子

-----------------------------------覆盖了自己的

$ dp[i][3] $ : 儿子孙子全部覆盖

$ dp[i][4] $ :孙子全部覆盖

-----------------------------------并没有覆盖自己

初始转移方程:

$ dp[i][0] = 1+\sum min(dp[j][0...4]) $ ;

要使选了根节点之后合法(整棵子树包括根节点被覆盖)必须使儿子的孙子全部覆盖, 0~4状态满足

$ dp[i][1] = min( dp[k][0] + \sum min(dp[j][0...3](j != k)) ) $ ;

要使选了一个儿子之后合法 由于儿子只可以覆盖到兄弟 所以孙子一定要全部被覆盖 即儿子的儿子一定覆盖 0~3满足

$ dp[i][2] = min( dp[k][1] + \sum min(dp[j][0...2](j != k)) ) $ ;

使选了一个孙子之后合法 由于孙子最多只能覆盖到当前节点 所以儿子一定全部覆盖 即所有儿子本身要被覆盖 0~2满足

$ dp[i][3] = \sum dp[j][0...2] $ ;

要使儿子及孙子全部被覆盖 即儿子本身要被覆盖 0~2满足

$ dp[i][4] = \sum dp[j][0...3]; $

要使孙子全部被覆盖 即儿子的儿子要全部被覆盖 0~3满足

注意:

每种状态由儿子转移过来所以根的情况 要转化成对于儿子来说的情况

然后改进状态 因为每种转移方程至少有三种可能最后取其中较小的 故时间效率较低

令 $ dp[i][k] $ 表示 $ min(dp[i][0],dp[i][1]....dp[i][k]) $ 且 $ k>=2 $ 因为上述转移方程最少都是0~2状态

那么转移方程就大幅度化简了:

$ dp[i][0] = 1+\sum dp[j][4] $ ;

直接由上面变形而来

$ dp[i][1] = dp[i][4] + min(dp[k][0]-dp[k][3]) $ ;

选一个儿子 需保证所有孙子被覆盖 即 $ dp[i][4] $ 然后要选出一个儿子 将他从0~3状态变为选了自己(由于 $ dp[i][4] $ 中他是3状态所以要减去一个 $ dp[k][3] $ ) 取这个差值最小的儿子

$ dp[i][2] = dp[i][3] + min(dp[k][1]-dp[k][2]) $ ;

选一个孙子 与上面类似 要保证所有儿子都被覆盖 即 $ dp[i][3] $ 再将一个儿子从02状态变为01状态以保证覆盖他父节点

$ dp[i][3] = \sum dp[j][2] $ ;

保证所有儿子被覆盖 儿子的0~2状态均符合条件

$ dp[i][4] = \sum dp[j][3] $ ;

保证所有儿子的儿子被覆盖 儿子的0~3状态均符合条件

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1005; inline int read(){
char ch = getchar();
int f = 1 , x = 0;
while(ch > '9' || ch < '0' ){if(ch == '-')f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + ch - '0';ch = getchar();}
return x * f;
} int n,a[maxn][maxn];
int f[maxn][5]; int main(){
n = read();
for(int i=2;i<=n;i++){
int x; x = read();
a[x][i] = 1;
}
for(int i=n;i>=1;i--){
int x1 = 1e8 , x2 = 1e9;
f[i][0] = 1;
for(int j=1;j<=n;j++){
if(a[i][j]){
f[i][0] += f[j][4];
f[i][3] += f[j][2];
f[i][4] += f[j][3];
x1 = min(x1 , f[j][0] - f[j][3]);
x2 = min(x2 , f[j][1] - f[j][2]);
}
}
f[i][1] = f[i][4] + x1;
f[i][2] = min(f[i][3] + x2 , min(f[i][0] , f[i][1]));
f[i][3] = min(f[i][2] , f[i][3]);
f[i][4] = min(f[i][3] , f[i][4]);
}
printf("%d",f[1][2]);
return 0;
}

洛谷P2279消防局的设立的更多相关文章

  1. 洛谷P2279 消防局的设立 [HNOI2003] 贪心

    正解:贪心 解题报告: 传送门! 这题贪心得挺显然的,,,?居然能有蓝,,,是蓝题太水了嘛,,,? 简单说下,这题一看到就能想到,对最低的没被覆盖到的点给它的祖父建一个消防局 没了? 哦这题实现还挺有 ...

  2. 洛谷P2279 消防局的设立【树形dp】

    题目:https://www.luogu.org/problemnew/show/P2279 题意:一棵树.在节点处建消防站,可以覆盖与他距离在2之内的节点.问最少要建多少个消防站,可以覆盖所有的节点 ...

  3. 洛谷 P2279 03湖南 消防局的设立

    2016-05-30 16:18:17 题目链接: 洛谷 P2279 03湖南 消防局的设立 题目大意: 给定一棵树,选定一个节点的集合,使得所有点都与集合中的点的距离在2以内 解法1: 贪心 首先D ...

  4. 【洛谷P2279】[HNOI2003]消防局的设立

    消防局的设立 题目链接 贪心:每次取出深度最大的节点,若没有被覆盖到,要想覆盖它, 最优的做法显然是将它的爷爷设为消防局 (因为该节点深度为最大,选兄弟.父亲所覆盖的节点,选了爷爷后都能够覆盖) 用优 ...

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

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

  6. 【题解】P2279消防局的设立

    [题解][P2279 HNOI2003]消防局的设立 又是一道贪心. 随便指定一个点为根,可以知道在覆盖了一个节点的子树的情况下,消防站越高越好.那么我们就贪心吧.\(trick\)是按深度\(pus ...

  7. 洛谷P2279 [HNOI2003]消防局的设立

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

  8. 洛谷 P2279 [HNOI2003]消防局的设立

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

  9. 洛谷 P2279 [HNOI2003]消防局的设立 题解

    每日一题 day34 打卡 Analysis 这道题的正解本来是树形dp,但要设5个状态,太麻烦了.于是我就用贪心试图做出此题,没想到还真做出来了. 考虑当前深度最大的叶子结点,你肯定要有一个消防局去 ...

随机推荐

  1. BZOJ 4321 queue2

    4321: queue2 Description n 个沙茶,被编号 1~n.排完队之后,每个沙茶希望,自己的相邻的两人只要无一个人的编号和自己的编号相差为 1(+1 或-1)就行: 现在想知道,存在 ...

  2. MVC中HTML控件设为只读readonly

    http://www.th7.cn/web/html-css/201501/78934.shtml 1.下拉框设为只读试了试用这个有效: @Html.DropDownListFor(model =&g ...

  3. 布隆过滤器 Bloom Filter

    使用普通集合来判断一个元素是否已存在于集合中,需要占用比较大的空间.而使用Bloom Filter 可有效节省空间. Bloom Filter 以较少的内存占用及较小的误判率达到判断元素是否存已经加入 ...

  4. C/C++ 移位计算代替乘除运算

    测试移位和乘除的比较,发现移位比乘除运算快一个位数的速度,但是难点在于判断是否是2的幂次级的数,如果不是还得通过代码拆分到2的幂次+上分子的累和,然后通过移位得到2的次幂数这样; 下列代码只是简单的判 ...

  5. 【Asp.net入门11】第一个ASP.NET 应用程序-创建摘要视图

    目前已经完成了应用程序的基本结构单元,受邀者也能够做出回复.这一节将添加一个支持组件,以显示收到的回复摘要,以便用户的朋友了解谁会参加晚会,并做出适当安排.在Solution Explorer中右键单 ...

  6. Java入门:读写文本文件

    文本文件的读写是学习java必须掌握的一项基本技术,因为在项目中时常会涉及到文本文件的读写. 一.使用FileWriter写文件 1.FileWriter类 [功能] FileWriter类专门用来写 ...

  7. sqlalchemy多外键关联

    一.前言 如果有张表A的多个字段关联另一张表B的一个字段,就如同一个客户表的账单地址和发货地址,同时关联地址表中的id字段. 二.事例 # -*- coding: UTF-8 -*- from sql ...

  8. bzoj千题计划126:bzoj1038: [ZJOI2008]瞭望塔

    http://www.lydsy.com/JudgeOnline/problem.php?id=1038 本题可以使用三分法 将点按横坐标排好序后 对于任意相邻两个点连成的线段,瞭望塔的高度 是单峰函 ...

  9. 穷竭搜索:POJ 3187 Backward Digit Sums

    题目:http://poj.org/problem?id=3187 题意: 像这样,输入N : 表示层数,输入over表示最后一层的数字,然后这是一个杨辉三角,根据这个公式,由最后一层的数,推出第一行 ...

  10. PHP7 学习笔记(一)Ubuntu 16.04 编译安装Nginx-1.10.3、 PHP7.0.9、Redis3.0 扩展、Phalcon3.1 扩展、Swoole1.9.8 扩展、ssh2扩展(全程编译安装)

    ==================== PHP 7.0 编译安装================== wget http://cn2.php.net/get/php-7.0.9.tar.bz2/fr ...