Apple Tree 【POJ - 2486】【树形DP】

题目链接

开始学习树形DP的第一题,算是无师自通吧。。。

题目求的是给你一棵数,以及能走的步数,问你走多能吃几颗苹果,如果化成一维的,很容易让人想到运用DP来解这道题,前一刻的步骤会影响后一刻的值,那么如今苹果被挂在了树上,我们就该用树形DP来解了。

树形DP及其推导:  这是道树形DP,题目如是讲到,于是就是处理它了,在这棵树上,我们想拿最多的苹果,首先我们知道从第u个节点向外走,如果一步都不剩了,那么他也能获得u节点上的苹果数,那么再往上,就是u->v,现在根在于v了,对于u,假如u的步数足够,那么它完全可以去v走一遭,甚至返回,去另外的节点,故,这里有个需要考虑的东西就是返回问题,所以DP的设定就得看到返回的问题了,我们即要拿这颗叶子结点上的苹果数,也要拿其他叶子的(就是这么个意思了)。

假如,目前从u点出发,我们怎么看?对于它之下的节点,我们假如已经确定了取值,毕竟dfs()先求到底,我们只要考虑u与u链接的子节点的关系就行,假如步数K足够,那么我们就可以往下走,类似背包的做法,我们可以做如下考虑:“u->v; v->u”就是最后会返回至u,这就比到v就停下来会多2步“u->v”到v就可以,就是u返回、而v不返回,最后就是v走下去的点,而v是u的子节点,所以就无须考虑了,此法会多出一步,因为要去v的那一步,“u->v; v->u”这步与第一步不同,它是在u出不返回,但在v处得返回,这样会多2步,因为要从v返回回来。

于是,列写DP方程:dp[i][j][k],其中,i为根,j为步数,k为0或1分别表示“不回来”和“回来”的两种情况。

u返回且v也返回情况:dp[u][j+2][1]=max(dp[u][j+2][1], dp[u][k][1] + dp[v][j-k][1]);

u返回而v不返回情况:dp[u][j+1][0]=max(dp[u][j+1][0], dp[u][k][1] + dp[v][j-k][0]);

u不返回而v返回情况:dp[u][j+2][0]=max(dp[u][j+2][0], dp[u][k][0] + dp[v][j-k][1])。

 

完整代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef long long ll;
const int maxN=105;
int N, K, dp[maxN][maxN<<1][2]={0}, apple[maxN];     //back_'1', no_back-'0'
vector<int> vt[maxN];
bool vis[maxN];
void dfs(int u)
{
    vis[u]=true;
    for(int i=0; i<=K; i++) dp[u][i][0]=dp[u][i][1]=apple[u];
    int len=(int)vt[u].size();
    for(int i=0; i<len; i++)
    {
        int v=vt[u][i];
        if(vis[v]) continue;
        dfs(v);
        for(int j=K; j>=0; j--)
        {
            for(int k=0; k<=j; k++)
            {
                dp[u][j+2][1]=max(dp[u][j+2][1], dp[u][k][1]+dp[v][j-k][1]);
                dp[u][j+1][0]=max(dp[u][j+1][0], dp[u][k][1]+dp[v][j-k][0]);
                dp[u][j+2][0]=max(dp[u][j+2][0], dp[u][k][0]+dp[v][j-k][1]);
            }
        }
    }
}
int main()
{
    while(scanf("%d%d", &N, &K)!=EOF)
    {
        for(int i=1; i<=N; i++){ vt[i].clear(); scanf("%d", &apple[i]); }
        memset(vis, false, sizeof(vis));
        for(int i=1; i<N; i++)
        {
            int e1, e2;
            scanf("%d%d", &e1, &e2);
            vt[e1].push_back(e2);
            vt[e2].push_back(e1);
        }
        dfs(1);
        printf("%d\n", max(dp[1][K][0], dp[1][K][1]));
    }
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值