poj2486

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

int n,k,tot;
int a[MAXN],h[MAXN];
int f[MAXN][MAXN<<1][2];//那个子树,移动几次  是否回到根 

struct node{
    int from,to,next;
}e[MAXN<<1];

void init(){
    tot=0;
    memset(h,-1,sizeof(h));
    memset(f,0,sizeof(f));
}

void add(int x,int y){
    tot++;
    e[tot].from=x;
    e[tot].to=y;
    e[tot].next=h[x];
    h[x]=tot;
}
//0表示回来根  1表示不必 
int dfs(int now,int fa){
    for(int i=0;i<=k;i++)f[now][i][0]=f[now][i][1]=a[now];
    for(int i=h[now];i!=(-1);i=e[i].next){
        if(e[i].to!=fa){
        dfs(e[i].to,now);    
        //开始dp了喔    
            for(int j=k;j>=0;j--){
                for(int p=1;p<=j;p++){
                    if(p>=2)f[now][j][0]=max(f[now][j][0],f[now][j-p][0]+f[e[i].to][p-2][0]);
                    if(p>=1)f[now][j][1]=max(f[now][j][1],f[now][j-p][0]+f[e[i].to][p-1][1]);
                    if(p>=2)f[now][j][1]=max(f[now][j][1],f[now][j-p][1]+f[e[i].to][p-2][0]);
                }
            }            
        }
    }
} 

int main(){
    while(scanf("%d%d",&n,&k)==2){
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        init();
        for(int i=1;i<n;i++){
            int x,y;scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        dfs(1,1);
        cout<<f[1][k][1]<<endl;
    }    
}
View Code

嘛,比较难想的dp(其实不是难想啦....就是复杂一点而已)

嘛直接先套树形背包模板  但发现不足以表达所有状态....

于是乎再加一维!!!!(是否一定在根节点)

然后转移就好了

1.一定在根节点

        1.其他子节点先到根,然后再转移当前子树

2.不一定在根节点

        1.先往其他子树绕一圈回来,然后回来后再直接往下面走

        2.从当前子树绕一圈回来,然后再接着走

然后这题就结束了

原文地址:https://www.cnblogs.com/shatianming/p/12299469.html