1. 程式人生 > >W - Apple Tree POJ - 2486 (樹形dp+揹包)

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;
}