#454 最大利润(树形dp)

本文介绍了一种使用树形DP方法解决特定问题的技术。通过定义状态f[i][0]和f[i][1]来分别表示不选择节点i和选择节点i时其子树的最大利润,并给出了详细的状态转移方程及其实现代码。

这里写图片描述

贯彻树形dp第一维通常是节点编号的思想,f[i]表示以i点为根的子树的最大利润。根据题意,相连的两点不能同时取,故可想到增加一维表示i点取或不取:f[ i ][ 0 ]表示不取,f[ i ][ 1 ]表示取。

那么我们就可以列出状态转移方程了:

f[ i ][ 1 ] = ∑ f[ x ][ 0 ] + value[ i ] ;
f[ i ][ 0 ] = ∑ max( f[ x ][ 0 ],f[ x ][ 1 ]);
(x∈child[i])

即,如果选择了i点,那么i的直接孩子都不可以选;若i点不选,那么它的孩子选不选都合法,取最大值即可。

具体求法采用递归。在树上,常常用递归而不是循环,因为转移顺序一般是由下到上。也符合了多个子问题合并为一个问题的思想。

void dp(int x)
{
    vis[x]=1;   
    for(int i=linkk[x];i;i=e[i].next)
        if(!vis[e[i].y])
        {
            dp(e[i].y);
            f[x][1]+=f[e[i].y][0];
            f[x][0]+=max(f[e[i].y][0],f[e[i].y][1]);
        }
    f[x][1]+=v[x];
}

void init()
{
    read(n);
    for(int i=1;i<=n;++i) read(v[i]);
    int x,y;
    for(int i=1;i<n;++i) 
    {
        read(x);read(y);
        insert(x,y);insert(y,x);
    }
}

void work()
{
    memset(f,0,sizeof(f));
    memset(vis,0,sizeof(vis));//因为无根树,无法确定点之间的顺序关系,只能存双向边,则需标记是否访问。
    dp(1);
    ans=max(f[1][1],f[1][0]);
    printf("%lld",ans);
}

int main()
{
    init();
    work();
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值