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

链接: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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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;
}


你可能感兴趣的:(*树形DP,*背包问题,DP)