洛谷 P2279 树形dp

题目链接

题目大意:给定一棵树,设一个关键节点可以覆盖半径为2的范围,问要覆盖整棵树要至少几个关键节点。

这道题可以用贪心或者树形dp解决。因为最近我深感自己dp能力太弱,所有练练dp。

状态设计:

f[p,0]代表将p的祖父覆盖需要的最少关键点数量

f[p,1]代表将p的父亲覆盖需要的最少数量

f[p,2]代表将p覆盖需要的最少数量

f[p,3]代表将p的儿子覆盖需要的最少数量

f[p,4]代表将p的孙子覆盖需要的最少数量

显而易见,f[p,0]≥f[p,1]≥f[p,2]≥f[p,3]≥f[p,4]

状态转移:

设s为p的一个儿子节点。

$$f\left[ p,0\right] =\sum ^{son}_{s}f\left[ s,4\right]$$

$$f\left[ p,1\right] =\min \left( \min \left\{ f\left[ s,0\right] +\sum ^{son}_{j\neq s}f\left[ j,3\right] \right\} ,f\left[ p,0\right] \right)$$

$$f\left[ p,2\right] =\min \left( \min \left\{ f\left[ s,1\right] +\sum ^{son}_{j\neq s}f\left[ j,2\right] \right\} ,f\left[ p,1\right] \right)$$

$$f\left[ p,3\right] =\min \left( \sum ^{son}_{s}f\left[ s,2\right] ,f\left[ p,2\right] \right)$$

$$f\left[ p,4\right] =\min \left( \sum ^{son}_{s}f\left[ s,3\right] ,f\left[ p,3\right] \right)$$

那么最终答案就是f[1,2]了。

 

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <queue>
#include <vector>
#include <list>
#include <set>
#include <map>
#define MAXN 1010
using namespace std;
int n,head[MAXN],to[MAXN*2],nxt[MAXN*2],tot=0,f[MAXN][5];

void connect(int u,int v)
{
    to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}

void dfs(int u)
{
    f[u][0]=1;f[u][3]=0;f[u][4]=0;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        dfs(v);
        f[u][0]+=f[v][4];
        f[u][3]+=f[v][2];
        f[u][4]+=f[v][3];
    }
    if(head[u]==0)
    {
        f[u][1]=f[u][2]=1;
        return;
    }
    for(int i=head[u];i;i=nxt[i])
    {
        int s=to[i];
        int f1=f[s][0],f2=f[s][1]; 
        for(int j=head[u];j;j=nxt[j])
        {
            if(j==i) continue;
            f1+=f[to[j]][3];
            f2+=f[to[j]][2];
        }
        f[u][1]=min(f[u][1],f1);
        f[u][2]=min(f[u][2],f2);
    }
    for(int i=1;i<=4;i++) f[u][i]=min(f[u][i],f[u][i-1]);
}

int main()
{
    memset(f,0x3f,sizeof(f));
    scanf("%d",&n);
    for(int i=2,v;i<=n;i++)
    {
        scanf("%d",&v);
        connect(v,i);
    }
    dfs(1);
    printf("%d\n",f[1][2]);
    return 0;
}

 

转载于:https://www.cnblogs.com/BakaCirno/p/11527336.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值