POJ2486 Apple Tree 树形DP

这题和POJ1947类似, 同样是树形DP, 就是因为按照上题的思路写, 导致写的是非常的蛋疼. 调了几小时还是无果. 时候证明还是对状态的意义理解的不透彻.

设dp[x][0][j]表示以节点x为根至多走j步并且返回x处所能够得到的最多苹果.
   dp[x][1][j]表示以节点x为根至多走j步不需要返回x处所能够得到的最多苹果.

那么就可以得到动态转移方程(此时的dp[x][..][j]还没有完善, 需要后期处理一下): 

dp[x][0][j] = max( dp'[x][0][j-m] + dp[v][0][m] );  0<=m<=j 其中dp'表示计算到上一个孩子节点为止, v是x的孩子节点
dp[x][1][j] = max( dp'[x][1][j-m] + dp[v][0][m],  dp'[x][0][j-m] + dp[v][1][m] ) 0<=m<=j

此时的dp[x][..][j] 并没有计算本身这个节点的权值, 并且进入这个节点同样有距离开销, 因此程序在实现的时候有一个平移更新状态的过程.

代码如下:

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;

/*
题意:给定一棵长满苹果的树, 告诉我们每个节点上有多少个苹果, 现在某人从1这个节点出
     发, 最多走K步的情况下, 最多能够得到多少苹果.
    
解法:
*/

struct Node {
    int v, next;    
}e[105];

int N, S, G[105][105], apple[105], vis[105];
int head[105], idx, ch[105], dp[105][2][205];

void insert(int a, int b) {
    ++idx;
    e[idx].v = b, e[idx].next = head[a];
    head[a] = idx;
}

void visit(int x) {
    vis[x] = 1;
    for (int i = 1; i <= N; ++i) {
        if (!vis[i] && G[x][i]) {
            insert(x, i);
            ++ch[x];
            visit(i);
        }
    }
}

void dfs(int x) {
    if (head[x] == -1) { // 如果该点位叶子节点
        return;
    }
    int temp[2][205] = {0}, v;
    for (int i = head[x]; i != -1; i = e[i].next) {
        dfs(v = e[i].v);
        for (int j = S; j >= 2; j--) {
            dp[v][0][j] = dp[v][0][j-2]+ apple[v];
        }
        dp[v][0][1] = dp[v][0][0] = dp[v][1][0] = 0;
        for (int j = S; j >= 1; j--) {
            dp[v][1][j] = dp[v][1][j-1]+ apple[v];
        }
         for (int j = 0; j <= S; ++j) {
            for (int m = 0; m <= j; ++m) {
                temp[0][j] = max(temp[0][j], dp[x][0][j-m] + dp[v][0][m]);
                temp[1][j] = max(temp[1][j], dp[x][0][j-m] + dp[v][1][m]);
                temp[1][j] = max(temp[1][j], dp[x][1][j-m] + dp[v][0][m]);
            }
        }
        memcpy(dp[x], temp, sizeof (temp));
    }
}

int main() {
    int x, y;
    while (scanf("%d %d", &N, &S) == 2) {
        idx = -1;
        memset(dp, 0, sizeof (dp));
        memset(head, 0xff, sizeof (head));
        memset(G, 0, sizeof (G));
        memset(ch, 0, sizeof (ch));
        memset(vis, 0, sizeof (vis));
        for (int i = 1;  i<= N; ++i) {
            scanf("%d", apple+i);    
        }
        for (int i = 1; i < N; ++i) {
            scanf("%d %d", &x, &y);
            G[x][y] = G[y][x] = 1;
        }
        visit(1); // 建立一棵从1为根的有根树
        dfs(1);
        printf("%d\n", dp[1][1][S] + apple[1]);
    }
    return 0;    
}
原文地址:https://www.cnblogs.com/Lyush/p/2865439.html