POJ 2486 Apple Tree 树形DP

题意:给一棵树,每个节点都有一个权值,权值只能拿一次,只让走K步,问最多能拿到多少权值

这个题目一开始没料到原来可以往回走,分析了样例才知道。之后我用DFS搜索,超时

后来一直想树形DP的状态转移,一直没想出来。后来还是看别人博客上的状态转移方程。

dp[0][rt][j+2]=max(本身,dp[0][rt][w]+dp[0][nx][j-w])//w为枚举的需要多少步,由于要从nx子节点返回,故需要比j多两步

dp[1][rt][j+2]=max(本身,dp[1][rt][w]+dp[0][nx][j-w])//走向子树后回来再一去不回,因为从子树回来需要多走两步

dp[1][rt][j+1]=max(自身,dp[1][nx][w]+dp[0][rt][j-w])//走向其他地方回来再走向子树一去不回,走子树只需要多一步

一开始没怎么理解,后来想通了,每次是枚举一个子树,已经枚举好的子树已经找到最佳状态,就是上面所谓的最佳状态,尚未枚举的子树暂且当做不存在

因为暂且未枚举的子树需要当做不存在,就要用到背包,从倒序循环步数,来保证当前找到的最佳,一定是该子树之前那个状态再加当前状态,我一开始正序循环步数,就像完全背包一样,同在一个状态里面,造成了干扰。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
vector<int> v[105];
int n,k;
int val[105];
int dp[2][105][205];
int vis[105];
void clean(int n)
{
    for (int i=0;i<=n;i++)
        v[i].clear();
}
void dfs(int rt)
{
    vis[rt]=1;
    int r=v[rt].size();
    for (int i=0;i<r;i++){
        int nx=v[rt][i];
        if (vis[nx]) continue;
        dfs(nx);
        for (int j=k;j>=0;j--){
           for (int w=0;w<=j;w++){
            if (dp[0][rt][j+2]<dp[0][rt][j-w]+dp[0][nx][w])
                dp[0][rt][j+2]=dp[0][rt][j-w]+dp[0][nx][w];

            if (dp[1][rt][j+2]<dp[1][rt][j-w]+dp[0][nx][w])
                dp[1][rt][j+2]=dp[1][rt][j-w]+dp[0][nx][w];

            if (dp[1][rt][j+1]<dp[1][nx][w]+dp[0][rt][j-w])
                dp[1][rt][j+1]=dp[1][nx][w]+dp[0][rt][j-w];

           }
        }
    }
}
int main()
{
    while (scanf("%d%d",&n,&k)!=EOF)
    {
        clean(n);
        int i,j;
        for (i=1;i<=n;i++)
            scanf("%d",&val[i]);
        for (j=1;j<n;j++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            v[a].push_back(b);
            v[b].push_back(a);
        }
        memset(vis,0,sizeof vis);
        for (i=0;i<=n+5;i++){
            for (j=0;j<=k+5;j++){
                dp[0][i][j]=val[i];
                dp[1][i][j]=val[i];
            }
        }
        dfs(1);
        int ans=0;
        ans=max(dp[0][1][k],dp[1][1][k]);
        printf("%d
",dp[1][1][k]);
    }
    return 0;
}

关于为什么最后最佳状态是dp[1][1][k]而不是dp[0][1][k],我也不是很理解,不过我想这样说可能说得通,如果是dp[0][1][k],则最后一步肯定是回到原点,这样没有对结果又任何贡献,最多也是==前者,所以最后最优结果应该是前者

原文地址:https://www.cnblogs.com/kkrisen/p/3404922.html