二叉苹果树(树形DP)

洛咕

题意:有一棵N个节点的二叉树,树根为1,现只能保留m根树枝,每根树枝连接两个节点,且有权值.求能够得到的最大权值和是多少.

分析:首先保留下来的树枝必须还是一棵树(可以不是二叉树),且树根节点必须保留.树上DP擅长于对节点的操作,于是保留m根树枝等价于保留m+1个节点.

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
int n,m;
int w[105][105],f[105][105],val[105],l[105],r[105];
void dfs1(int u){
    for(int i=1;i<=n;i++){//构建左子树
        if(w[u][i]>=0){
            val[i]=w[u][i];
//把边u-->i上的权值赋给节点i
            l[u]=i;
//记录它的左儿子
            w[u][i]=w[i][u]=-1;
//做个标记,不再被选
            dfs1(i);
//递归处理左子树
            break;
//break很关键,对于节点u它要么有两个儿子,要么没儿子
//此时我们已经找到它的左儿子,那么一定要break掉
//才能进入下面的循环,找到右儿子.
        }
    }
    for(int i=1;i<=n;i++){//构建右子树
        if(w[u][i]>=0){
            val[i]=w[u][i];
            r[u]=i;
            w[u][i]=w[i][u]=-1;
            dfs1(i);
            break;
        }
    }
    return;
}
//以x为根的树上,保留y个节点获得的最大权值和
int dfs2(int x,int y){
    if(y==0)return 0;//一个都不能选,自然为0
    if(l[x]==0&&r[x]==0)return val[x];
//如果没有左子树和右子树,就只能选自己获得最大权值.
    if(f[x][y])return f[x][y];//记忆化
    for(int i=0;i<=y-1;i++){//枚举保留节点数量
        f[x][y]=max(f[x][y],dfs2(l[x],i)+dfs2(r[x],y-1-i)+val[x]);
    }
    return f[x][y];
}
int main(){
    n=read();m=read();m++;
    memset(w,-1,sizeof(w));
    for(int i=1;i<n;i++){
        int a=read(),b=read(),c=read();
        w[a][b]=w[b][a]=c;//数据小,直接邻接矩阵
    }
    dfs1(1);//建树
    printf("%d\n",dfs2(1,m));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/PPXppx/p/10542999.html