POJ 2486 Apple Tree 树形DP+分组背包

6 篇文章 0 订阅
4 篇文章 0 订阅

链接:http://poj.org/problem?id=2486

题意:一棵(苹果)树,树上有N个结点(N<=100),起点是结点1。每个结点上有若干个苹果,我可以进行K步操作(K<=200),每次操作是从当前结点移动到相邻的结点,并且到了相邻的结点以后会吃掉上面的所有苹果并且苹果不再长出来,相邻是指两个结点之间有边相连。问在K步操作之后最多可以吃掉多少个苹果。

思路:刚入手的时候觉得是一般的树形背包问题,dp[i][j]代表的是以i为根的子树中走j个结点所能吃到的苹果数,来进行状态转移,但这样转移的话每次的转移消耗就是2(一来一回),但是有可能出现的情况是不回到起点直接到某个点就停下不向前走。

这样上面的状态转移就不能满足要求。所以要扩展为dp[i][j][k]。

k=0,代表的是最后停在了i的子树中不回到结点i;k=1,代表的是在i的子树中走了j个结点并且最后回到了结点j。

状态转移方程:

dp[u][i+2][1]=max(dp[u][i+2][1],dp[u][i-j][1]+dp[v][j][1]);表示回到根结点的情况可以通过回到子结点的情况推得。
dp[u][i+1][0]=max(dp[u][i+1][0],dp[u][i-j][1]+dp[v][j][0]);表示不回到根结点的情况可以通过将原来回到根结点的情况由不回到子结点的情况更新得到。
dp[u][i+2][0]=max(dp[u][i+2][0],dp[u][i-j][0]+dp[v][j][1]);表示不回到根结点的情况可以通过将原来不回到根结点的情况由回到子结点的情况更新得到。

这个分组背包表示一种思想上的分组,因为dp[i][j][0]与dp[i][j][1]代表的两种情况是矛盾的,一定是不能同时出现。最后更新回到根结点时,所有状态中的最大值即为答案。

代码:

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <ctype.h>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define eps 1e-8
#define INF 0x3fffffff
#define maxn 105
#define maxm 205
#define PI acos(-1.0)
#define seed 31//131,1313
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
int head[maxn],top,ans,dp[maxn][maxm][2],N,K,x,y,a[maxn];
void init()
{
    memset(head,-1,sizeof(head));
    memset(dp,0,sizeof(dp));
    memset(a,0,sizeof(a));
    top=0;
    ans=0;
}
struct Edge
{
    int v;
    int next;
} edge[maxn*2];
void add_edge(int u,int v)
{
    edge[top].v=v;
    edge[top].next=head[u];
    head[u]=top++;
}
void dfs(int u,int f)
{
    dp[u][0][0]=dp[u][0][1]=a[u];
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==f)
            continue;
        dfs(v,u);
        for(int i=K;i>=0;i--)
        {
            for(int j=i;j>=0;j--)
            {
                dp[u][i+2][1]=max(dp[u][i+2][1],dp[u][i-j][1]+dp[v][j][1]);
                dp[u][i+1][0]=max(dp[u][i+1][0],dp[u][i-j][1]+dp[v][j][0]);
                dp[u][i+2][0]=max(dp[u][i+2][0],dp[u][i-j][0]+dp[v][j][1]);
            }
        }
    }
}
int main()
{
    while(~scanf("%d%d",&N,&K))
    {
        init();
        for(int i=1; i<=N; i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=N-1;i++)
        {
            scanf("%d%d",&x,&y);
            add_edge(x,y);
            add_edge(y,x);
        }
        dfs(1,1);
        int ans=0;
        for(int i=0;i<=K;i++)
        {
            if(dp[1][i][0]>ans)
                ans=dp[1][i][0];
            if(dp[1][i][1]>ans)
                ans=dp[1][i][1];
        }
        printf("%d\n",ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值