poj2486

/*
    题意:给你一个树, 树上的每个节点都会有点权, 现在小红帽从1节点开始去其他子树, 小红帽每到一个地方便拿走这个树的结点的点权
    ,小红帽可以回来, 问你小红帽在不超过k步的情况下拿走的最大点权是多少?
    tag:树形dp
    分析:我们定义dp[u][k][0]为从u结点到各个子树走不超过k步回来的情况下拿到的最大权值, dp[u][k][1]为从u结点到各个子树不超过k步不回来
    的情况下拿到的最大权值。那么dp[u][k][0] = max(dp[v][j-2][0] + dp[u][k-j][0])
    dp[u][k][1] = max(dp[v][j-1][1] + dp[u][k-j][0])
    dp[u][k][1] = max(dp[v][j-2][0] + dp[u][k-j][1])
    注意计算顺序,我们要将k从大到小枚举才能保证走其他子树的时候不走v这个子树。
*/

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>

using namespace std;
const int maxn = 200 + 10;
int N, K;
int nweight[maxn];
vector<int> G[maxn];

int dp[maxn][maxn][2];

void dfs(int fa, int u) {
    for(int i=0; i<=K; i++)
        dp[u][i][0] = dp[u][i][1] = nweight[u];
    for(int i=0; i<G[u].size(); i++) {
        int v = G[u][i];
        if(v == fa) continue;
        dfs(u, v);
        for(int k=K; k>=0; k--)    //从大到小防止重复计算
            for(int j=0; j<=k; j++) {
                int ans = dp[u][k-j][0];
                if(j-2>=0) ans += dp[v][j-2][0];
                dp[u][k][0] = max(dp[u][k][0], ans);
                ans = dp[u][k-j][0];
                if(j-1>=0) ans += dp[v][j-1][1];
                dp[u][k][1] = max(dp[u][k][1], ans);
                ans = dp[u][k-j][1];
                if(j-2>=0) ans += dp[v][j-2][0];
                dp[u][k][1] = max(dp[u][k][1], ans);
            }
    }
}

int main() {
    while(scanf("%d%d", &N, &K) == 2) {
        for(int i=1; i<=N; i++){
            scanf("%d", &nweight[i]);
            G[i].clear();
        }
        for(int i=1; i<=N-1; i++) {
            int u, v; scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(-1, 1);
        int ans = max(dp[1][K][0], dp[1][K][1]);
        printf("%d\n", ans);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值