POJ 2486 (树形背包 入门题)

题意:

           一颗树,n个点(1-n),n-1条边,每个点上有一个权值,求从1出发,走k步,最多能遍历到的权值


思路:

1.我们想要回到根节点。
         把这个过程分为两个部分:
         step1:从根节点x到达son,从son遍历son的子树再回到son,从son回到son的根节点x;
         step2:用剩余的步数遍历根节点x其余的子树,再回到根节点x.
         获得的价值:dp[son][s-2][0] + dp[x][j - s][0]
         为什么是s - 2呢,因为除过son的分支外,根的其他分支已经用去了j-s步,到son只剩下s步,而来回从根到son需要两步。
         所以在以son为根的子树中走的就是s-2步;
         我们得到: dp[x][j][0] = max(dp[x][j][0] , dp[son][s-2][0] + dp[x][j - s][0]) 

2.不一定回到根节点。
         既然不一定回到根节点x,那么最后的位置会是哪里呢?
         首先要把以当前决策的子节点son为根节点的子树和根节点x的其余子树分开来考虑。
         case1:
         最后的位置在以子节点son为根节点的树上。
         那么:
         step1:从根节点遍历其余的子树,最终回到根节点;
         step2:从根节点遍历以son为根节点的树,最终不一定回到son.
         我们得到:  dp[x][j][1] = max(dp[x][j][1] , dp[son][s-1][1] + dp[x][j - s][0])
         多出的那个“1”和上面类似,是从根节点x到子节点son的一步。
         case2:
         最后的位置在其余的子树上。
         step1:从根节点遍历以son为根节点的子树,最终回到son,并回到根节点x.
         strep2:从根节点出发遍历其余的子树,最终不一定回到son,不知所踪。
         我们得到 : dp[x][j][1] = max(dp[x][j][1] , dp[son][s-2][0] + dp[x][j- s][1])
状态转移方程大功告成。

还有因为题目没有说必须走k步,所以每个节点都初始化为a[x];

ac代码:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=100+10;

int dp[maxn][maxn*2][2];

int head[maxn*2];
int cnt;
int a[maxn];
int n,k;

struct node{
    int v;
    int next;
    int w;
}p[maxn*2];

inline void addedge(int u,int v){
    p[++cnt].v=v;
    p[cnt].next=head[u];
    head[u]=cnt;
}

inline void init(){
    cnt=0;
    memset(head,-1,sizeof(head));
    memset(dp,0,sizeof(dp));
}

void dfs(int x,int fa){
    for(int i=0;i<=k;i++){
        dp[x][i][0]=dp[x][i][1]=a[x];
    }
    for(int e=head[x];e!=-1;e=p[e].next){
        int son=p[e].v;
        if(son==fa) continue;
        dfs(son,x);
        for(int j=k;j>=0;j--){
            for(int s=1;s<=j;s++){
                if(s>=2){
                    dp[x][j][0]=max(dp[x][j][0],dp[x][j-s][0]+dp[son][s-2][0]);
                }
                dp[x][j][1]=max(dp[x][j][1],dp[x][j-s][0]+dp[son][s-1][1]);
                if(s>=2){
                    dp[x][j][1]=max(dp[x][j][1],dp[x][j-s][1]+dp[son][s-2][0]);
                }
            }
        }
    }
}

int main(){
    while(scanf("%d%d",&n,&k)!=EOF){
        init();
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int u,v;
        for(int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        dfs(1,0);
        printf("%d\n",max(dp[1][k][0],dp[1][k][1]));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值