POJ 2486 树形DP+背包

题意

一棵树,树上有N个点,能走K步。第二行有N个数字,代表着N个点上的苹果数。后面N-1行代表苹果树的联通情况。问最多能得到多少个苹果。

题解

又是一道树形DP的题目,不过这道题不太好搞。这道题涉及的情况还是比较复杂的,需要十分缜密的思维。首先我们可以把状态转移方程设置为dp[i][j][k],i代表当前点,j代表当前状态还可走的步数,k分两种状态,0代表回到当前点,1代表不回到当前点。
对于回到当前点的情况,还是比较容易分析的。dp[u][j][0]=max(dp[u][j][0],dp[u][j-s-2][0]+dp[v][s][0])。因为向下走一步和回来分别消耗了1步,因此便需要j-s-2,这代表从某个点回来以后还可以访问其他的点,从其他的点再回来。
对于不回到当前点的情况,就有些难分析了,稍有不慎,很容易就会WA。不回到当前点,分两种状态转移情况,一种情况是从别的点回来以后再访问下一个点,下一个点不再回来。这个对应状态转移方程dp[u][j][1]=max(dp[u][j][1],dp[u][j-s-1][0]+dp[v][s][1])。另一种情况是当前这个点回来,选择前面访问过的点作为不回来的点。这个对应状态转移方程dp[u][j][1]=max(dp[u][j][1],dp[u][j-s-2][1]+dp[v][s][0])。只有考虑完整了这两个状态,才能保证结果是最大的。

代码

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#include<stack>
#include<string>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define LL long long
#define INF 0x3f3f3f3f
#define MAXN 800010
#define MOD 1000000007
#define EPS 1e-3
using namespace std;
int dp[110][210][2];
vector<int> vc[110];
int n,k;
int val[110];

void dfs(int u,int p) {
    int sz=vc[u].size();
    UP(i,0,k+1) {
        dp[u][i][0]=dp[u][i][1]=val[u];
    }
    UP(i,0,sz) {
        int v=vc[u][i];
        if(v==p)
            continue;
        dfs(v,u);
        DOWN(j,k+1,1) {
//            cout<<j<<endl;
            UP(s,0,k+1) {
                if(j-s-2>=0) {
                    dp[u][j][0]=max(dp[u][j][0],dp[u][j-s-2][0]+dp[v][s][0]);
                    dp[u][j][1]=max(dp[u][j][1],dp[u][j-s-2][1]+dp[v][s][0]);
                }
                if(j-s-1>=0) {
                    dp[u][j][1]=max(dp[u][j][1],dp[u][j-s-1][0]+dp[v][s][1]);
                }
//                cout<<u<<" "<<s<<" "<<v<<" "<<dp[u][j][1]<<endl;
            }
        }
    }
}

int main() {
    W(~scanf("%d%d",&n,&k)) {
        MEM(dp,0);
        MEM(vc,0);
        UP(i,1,n+1) {
            scanf("%d",&val[i]);
        }
        UP(i,1,n) {
            int a,b;
            scanf("%d%d",&a,&b);
            vc[a].push_back(b);
            vc[b].push_back(a);
        }
        dfs(1,-1);
        printf("%d\n",max(dp[1][k][0],dp[1][k][1]));
    }
}

/*
5 5
2 15 5 15 10
1 2
2 3
2 4
3 5

5 5
0 3 4 4 1
1 2
1 3
3 4
2 5
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值