W - Apple Tree POJ - 2486 (树形dp+背包)

W - Apple Tree

 POJ - 2486 

Wshxzt is a lovely girl. She likes apple very much. One day HX takes her to an apple tree. There are N nodes in the tree. Each node has an amount of apples. Wshxzt starts her happy trip at one node. She can eat up all the apples in the nodes she reaches. HX is a kind guy. He knows that eating too many can make the lovely girl become fat. So he doesn’t allow Wshxzt to go more than K steps in the tree. It costs one step when she goes from one node to another adjacent node. Wshxzt likes apple very much. So she wants to eat as many as she can. Can you tell how many apples she can eat in at most K steps.

Input

There are several test cases in the input 
Each test case contains three parts. 
The first part is two numbers N K, whose meanings we have talked about just now. We denote the nodes by 1 2 ... N. Since it is a tree, each node can reach any other in only one route. (1<=N<=100, 0<=K<=200) 
The second part contains N integers (All integers are nonnegative and not bigger than 1000). The ith number is the amount of apples in Node i. 
The third part contains N-1 line. There are two numbers A,B in each line, meaning that Node A and Node B are adjacent. 
Input will be ended by the end of file. 

Note: Wshxzt starts at Node 1.

Output

For each test case, output the maximal numbers of apples Wshxzt can eat at a line.

Sample Input

2 1 
0 11
1 2
3 2
0 1 2
1 2
1 3

Sample Output

11
2

题意:从数根开始走,每到一个结点都有一定数量的苹果可以收集,问最多走p步最多能收集多少苹果;

思路:定义dp[u][j][0]表示从结点u出发可以走j步并且不回到结点u最多能收集的苹果,定义dp[u][j][0]表示从结点u出发可以走j步并且回到结点u最多能收集的苹果,dfs对于每个子树v,转换到了分组背包问题上,取步数j=p->1;可以得到如下状态转移方程:

                 dp[u][j][0]=max(dp[u][j][0],dp[u][j-k][1]+dp[to][k-1][0]);  //在它子树走j-k步并且回到结点u,然后从u出发,从u到v要走一步,在子树v上走k-1步,
                if(k>=2)
                {
                    dp[u][j][0]=max(dp[u][j][0],dp[to][k-2][1]+dp[u][j-k][0]);  //u->v走1步,在子树上走k-2步,然后v-u走1步,剩下j-k步在其他子树上走,不用回来
                    dp[u][j][1]=max(dp[u][j][1],dp[to][k-2][1]+dp[u][j-k][1]);  //同理
                }

其中k为分给到当前子树v的步数

背包问题:dp[u][j-k][0]或dp[u][j-k][1]的值(非零的值)来自遍历子树v之前遍历其他子树更新的值,因而dp[u][j-k][0]或dp[u][j-k][1]的实际意义可以理解为从其他子树上走j-k步回到u或不回到u收集的最多苹果树。而如果j的值从1到p,那么dp[u][j-k][0]或dp[u][j-k][1]的值可能来自前面的j=j-k,由子树v更新的值,而当前的j又更新一次,那么如果更新的值dp[u][j][1]或dp[u][j][0]发生变化,那么在子树上就重复遍历了一次,这个在背包问题上深有体会。

#include<cstdio>
#include<stack>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<iostream>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
const int nmax=44;
const double esp = 1e-9;
const double PI=3.1415926;
const int N=222;
int n,p;
int dp[N][N][2],sum[N],head[N];
struct node
{
    int v,next;
} edge[N];
int cnt;
void add(int u,int v)
{
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u,int pre)
{
    for(int i=0; i<=p; i++)
        dp[u][i][1]=dp[u][i][0]=sum[u];  
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        int to=edge[i].v;
        if(to==pre)
            continue;
        dfs(to,u);
        for(int j=p; j>=1; j--)
        {
            for(int k=1; k<=j; k++)
            {
                dp[u][j][0]=max(dp[u][j][0],dp[to][k-1][0]+dp[u][j-k][1]);
                if(k>=2)
                {
                    dp[u][j][0]=max(dp[u][j][0],dp[to][k-2][1]+dp[u][j-k][0]);
                    dp[u][j][1]=max(dp[u][j][1],dp[to][k-2][1]+dp[u][j-k][1]);
                }
            }
        }

    }
}
int main()
{
    int x,y;
    while(scanf("%d%d",&n,&p)!=EOF)
    {
        cnt=0;
        memset(head,-1,sizeof(head));
        memset(dp,0,sizeof(dp));
        for(int i=1; i<=n; i++)
            scanf("%d",&sum[i]);
        for(int i=1; i<n; i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        dfs(1,0);
        printf("%d\n",dp[1][p][0]);  
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/clz16251102113/article/details/83686301
今日推荐