[HNOI 2003] 消防局的设立
洛谷 2279 03 湖南 消防局的设立
本题地址: http://www.luogu.org/problem/show?pid=2279
题目描述
2020 年,人类在火星上建立了一个庞大的基地群,总共有 n 个基地。起初为了节约材料,人类只修建了 n-1 条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地 A 到基地 B 至少要经过 d 条道路的话,我们称基地 A 到基地 B 的距离为 d。
由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过 2 的基地的火灾。
你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。
输入输出格式
输入格式:
输入文件名为 input.txt。
输入文件的第一行为 n (n<=1000),表示火星上基地的数目。接下来的 n-1 行每行有一个正整数,其中文件第 i 行的正整数为 a[i],表示从编号为 i 的基地到编号为 a[i] 的基地之间有一条道路,为了更加简洁的描述树状结构的基地群,有
输出格式:
输出文件名为 output.txt
输出文件仅有一个正整数,表示至少要设立多少个消防局才有能力及时扑灭任何基地发生的火灾。
输入输出样例
输入样例 #1:
6
1
2
3
4
5
输出样例 #1:
2
思路
很有意思的一道(shui)题,但是对于某蒟蒻来说,也是想了很久。。。T_T
树形 dp,就是 dfs+ 向上更新。比较难想到的是状态,这里每种节点用了 5 个 dp[v][0~4],分别代表某个节点 v** 至少覆盖到孙子、儿子、自己、父亲、祖父节点 ** 所需的消防站个数。
然后是状态转移。从覆盖到祖父开始,很容易看出来,这个节点 v 自己一定要涂色(设消防局),向上两个距离,向下两个距离,所以 v 的儿子们只要管到各自的孙子就行了。画张图理解下:
如果要求覆盖到父亲,那么一种情况就是同上,因为这 5 种状态是包含的关系,而不是排斥的。第二种情况,就是 v 的某个儿子覆盖到它的祖父(一起唱:儿子的爷爷是爸爸),由于那个儿子可以向上两个距离,同时也可以绕下来覆盖 v 的其他的儿子节点,从而 v 的其他儿子只要管到各自的儿子就行了。
如果要求覆盖到自己,第一种情况也包含在上面。第二种情况,v 的某个儿子覆盖到 v(就是他的父亲),其余的儿子管到自身。
之后的以此类推←_←具体见代码
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 3000, INF = 0x3f3f3f3f;
int head[MAXN], next[MAXN], to[MAXN], pa[MAXN], tot, N;
bool lf[MAXN]; //是否为叶子节点
int dp[MAXN][10]; //树状dp
//dp[v][n] = 从n=0时为v的孙子节点,到n=4时为v的祖父节点,覆盖到第n层所需的最少数目
void init(){
memset(head, -1, sizeof(head));
tot = 0;
}
void add2(int a, int b){
to[tot] = b; next[tot] = head[a]; head[a] = tot++;
to[tot] = a; next[tot] = head[b]; head[b] = tot++;
}
void pushup(int v){ //将v的子节点的信息更新到v
dp[v][4] = 1;
int min1 = INF, min2 = INF;
for(int i = head[v]; ~i; i = next[i]){
int u = to[i];
if(u != pa[v]){
dp[v][4] += dp[u][0];
// dp[v][3] += dp[u][1];
// dp[v][2] += dp[u][2];
dp[v][1] += dp[u][2];
dp[v][0] += dp[u][1];
min1 = min(min1, dp[u][4] - dp[u][1]);
min2 = min(min2, dp[u][3] - dp[u][2]);
}
}
dp[v][3] = dp[v][0] + min1;
dp[v][2] = dp[v][1] + min2;
for(int i = 3; i >= 0; --i){
dp[v][i] = min(dp[v][i], dp[v][i+1]);
}
}
void dfs(int v){
if(lf[v]){
dp[v][4] = dp[v][3] = dp[v][2] = 1;
return;
}
for(int i = head[v]; ~i; i = next[i]){
int u = to[i];
if(u != pa[v]){
dfs(u);
}
}
pushup(v);
}
int main(){
freopen("in.txt", "r", stdin);
scanf("%d", &N);
if(N == 0){
printf("0\n");
return 0;
}
init();
for(int i = 2; i <= N; ++i){
int p;
scanf("%d", &p);
pa[i] = p;
lf[p] = false;
add2(i, p);
}
dfs(1);
printf("%d\n", dp[1][2]);
return 0;
}