首页 > 代码库 > zoj 3626 Treasure Hunt I (树形dp)

zoj 3626 Treasure Hunt I (树形dp)

题目大意:

给出一棵树,求出从起点开始走m长度最后回到起点,所能得到的宝藏的最大价值。


思路分析:

通过一次dfs可以得到的是子树到根节点的所有距离的最大值。

现在的问题就是他走完一颗子树可以去另外一颗子树。

所以在回溯到根的时候要统计其他子树上互补距离的最大值。


dp[i] [j] 表示i为根节点,在i的子树中走j步然后回到i所能拿到的最大价值。

转移方程就是

dp[x][i+2*len]=max(dp[x][i+2*len],dp[v][j]+dp[x][i-j]);

v为x的子树的根,len 为 x-v的距离 


#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstring>
#define maxn 105
using namespace std;

int dp[maxn][maxn<<1];
int val[maxn];
int n,m,k;

int tot;
int head[maxn];
int next[maxn<<1];
int to[maxn<<1];
int cost[maxn<<1];

void init()
{
    tot=0;
    memset(head,0,sizeof head);
}
void addedge(int u,int v,int w)
{
    tot++;
    next[tot]=head[u];
    to[tot]=v;
    cost[tot]=w;
    head[u]=tot;
}
bool vis[maxn];
void dfs(int x)
{
    for(int i=0;i<=m;i++)
        dp[x][i]=0;
    for(int p=head[x];p;p=next[p])
    {
        int v=to[p];
        if(vis[v])continue;
        int len=cost[p];
        vis[v]=true;
        dfs(v);
        for(int i=m;i>=0;i--)
        {
            if(i+2*len>m)continue;
            for(int j=0;j<i;j++)
                dp[x][i+2*len]=max(dp[x][i+2*len],dp[v][j]+dp[x][i-j]);
        }
        for(int i=len*2;i<=m;i++)
            dp[x][i]=max(dp[x][i],dp[v][i-len*2]);
    }
    for(int i=0;i<=m;i++)
        dp[x][i]+=val[x];
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        init();
        for(int i=1;i<=n;i++)scanf("%d",&val[i]);
        for(int i=2;i<=n;i++)
        {
            int l,r,w;
            scanf("%d%d%d",&l,&r,&w);
            addedge(l,r,w);
            addedge(r,l,w);
        }
        scanf("%d%d",&k,&m);
        memset(vis,false,sizeof vis);
        vis[k]=true;
        dfs(k);
        int ans=0;
        for(int i=0;i<=m;i++)
        {
            ans=max(ans,dp[k][i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}