树形 DP

文章目录

消防局的设立

题目:

  • 2020 2020 2020 年,人类在火星上建立了一个庞大的基地群,总共有 n n n 个基地,编号 1 ∼ n 1∼n 1n
    起初为了节约材料,人类只修建了 n − 1 n−1 n1 条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。
    如果基地 A A A 到基地 B B B 至少要经过 d d d 条道路的话,我们称基地 A A A 到基地 B B B 的距离为 d d d
    由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。
    消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过 2 2 2 的基地的火灾。
    你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。
    数据范围
    1≤n≤1000

dp方程式:

  • f [ u ] [ 0 ] : f[u][0] : f[u][0]: 表示u的子树满足且向上的父亲的父亲满足。
  • f [ u ] [ 1 ] : f[u][1] : f[u][1]: 表示u的子树满足且向上的父亲满足。
  • f [ u ] [ 2 ] : f[u][2] : f[u][2]: 表示u的所有的子树满足。
  • f [ u ] [ 3 ] : f[u][3] : f[u][3]: 表示u的所有儿子的子树满足。
  • f [ u ] [ 4 ] : f[u][4] : f[u][4]: 表示u的所有儿子的儿子的子树满足。

状态转移:

  • f [ u ] [ 0 ] = 1 + ∑ f[u][0] = 1 + ∑ f[u][0]=1+v f [ v ] [ 1 ] f[v][1] f[v][1]
  • f [ u ] [ 1 ] = m i n ( f [ v ] [ 0 ] + ∑ f [ v ′ ] [ 3 ] ) f[u][1] = min(f[v][0] + ∑f[v'][3]) f[u][1]=min(f[v][0]+f[v][3])
  • f [ u ] [ 2 ] = m i n ( f [ v ] [ 1 ] + ∑ f [ v ′ ] [ 2 ] ) f[u][2] = min(f[v][1] + ∑f[v'][2]) f[u][2]=min(f[v][1]+f[v][2])
  • f [ u ] [ 3 ] = ∑ f [ v ] [ 2 ] f[u][3] = ∑f[v][2] f[u][3]=f[v][2]
  • f [ u ] [ 4 ] = ∑ f [ v ] [ 3 ] f[u][4] = ∑f[v][3] f[u][4]=f[v][3]

最后dp更新,上层满足的下层也满足。

  • f [ u ] [ 1 ] = m i n ( f [ u ] [ 1 ] , f [ u ] [ 0 ] ) ; f[u][1] = min(f[u][1],f[u][0]); f[u][1]=min(f[u][1],f[u][0]);
  • f [ u ] [ 2 ] = m i n ( f [ u ] [ 2 ] , f [ u ] [ 1 ] ) ; f[u][2] = min(f[u][2],f[u][1]); f[u][2]=min(f[u][2],f[u][1]);
  • f [ u ] [ 3 ] = m i n ( f [ u ] [ 3 ] , f [ u ] [ 2 ] ) ; f[u][3] = min(f[u][3],f[u][2]); f[u][3]=min(f[u][3],f[u][2]);
  • f [ u ] [ 4 ] = m i n ( f [ u ] [ 4 ] , f [ u ] [ 3 ] ) ; f[u][4] = min(f[u][4],f[u][3]); f[u][4]=min(f[u][4],f[u][3]);

代码

#include<iostream>
#include<cstring>
using namespace std;
const int N = 1100;
int f[N][6];
int h[N],e[N<<1],ne[N<<1],idx;
void add(int a,int b){
    ne[idx] = h[a],e[idx] = b,h[a] = idx++;
}
int n,tp;
//f[u][0] = 1 + sum:f[v][4];
//f[u][1] = min(f[v][0] + sum:f[v'][3]);
//f[u][2] = min(f[v][1] + sum:f[v'][2]);
//f[u][3] = sum:f[v][2];
//f[u][4] = sum:f[v][3];

void dfs(int u,int fa){
    bool leaf = 1;
    int sum1,sum2,sum3;
    sum1 = sum2 = sum3 = 0;
    for(int i = h[u];~i;i=ne[i]){
        int y = e[i];
        if(y == fa)continue;
        leaf = 0;
        dfs(y,u);
        sum1 += f[y][4];
        sum2 += f[y][3];
        sum3 += f[y][2];
    }
    if(leaf){f[u][0] = f[u][1] = f[u][2] = 1;return ;}
    
    int mn1=0x3f3f,mn2=0x3f3f;
    for(int i = h[u];~i;i=ne[i]){
        int y = e[i];   
        if(y == fa)continue;
        mn1 = min(mn1,f[y][0] + sum2 - f[y][3]);
        mn2 = min(mn2,f[y][1] + sum3 - f[y][2]);
    }
    f[u][0] = 1 + sum1;
    f[u][1] = mn1;
    f[u][2] = mn2;
    f[u][3] = sum3;
    f[u][4] = sum2;
    
    
    f[u][1] = min(f[u][1],f[u][0]);
    f[u][2] = min(f[u][2],f[u][1]);
    f[u][3] = min(f[u][3],f[u][2]);
    f[u][4] = min(f[u][4],f[u][3]);
    
}
int main(){
    cin >> n;
    memset(h,-1,sizeof h);
    for(int i = 2;i<=n;i++){
        cin >> tp;
        add(i,tp);
        add(tp,i);
    }
    dfs(1,-1);
    cout << f[1][2] << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值