树形DP


顾名思义:在树上进行DP

思想: 把每一个节点当成一个包,包套包,以每一个节点的儿子为范围进行dp

例题:

题目描述
有一棵点数为 nn 的树,树边有边权。给你一个在 0 \sim n0~n 之内的正整数 kk ,你要在这棵树中选择 kk 个点,将其染成黑色,并将其他 的 n-kn?k 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。

输入格式
第一行包含两个整数 n,kn,k。

第二到 nn 行每行三个正整数 fr, to, disfr,to,dis,表示该树中存在一条长度为 disdis 的边 (fr, to)(fr,to)。输入保证所有点之间是联通的。

输出格式
输出一个正整数,表示收益的最大值。

输入输出样例
输入 #1复制
3 1
1 2 1
1 3 2
输出 #1复制
3
说明/提示
对于 100\%100% 的数据,0 \leq n,k \leq 20000≤n,k≤2000
View topic

代码:仔细看,仔细想

#include 
using namespace std;
#define ri register int
#define M 2005

long long f[M][M];
struct edge{
    int val,v;
    edge (){}
    edge(int a,int b){v=a,val=b;}
};
vector  p[M];
int T;int n,m; 
int sz[M],vis[M];
void dfs(int u)
{
    
    sz[u]=1;
    vis[u]=1;
    f[u][0]=0;f[u][1]=1;//?? 仔细思考 
    for(ri i=0;i)
    {
        int v=p[u][i].v;
        if(vis[v])continue ;
        dfs(v);
        sz[u]+=sz[v];
        for(ri j=min(T,sz[u]);j>=0;j--)
        {
            for(ri k=0;k<=min(j,sz[v]);k++)
            {
                if(f[u][j-k]==-1) continue ; // 类似于装满问题,很重要,排除不合理的情况; 
                long long val=1ll*p[u][i].val*(T-k)*k+1ll*p[u][i].val*(sz[v]-k)*(n-T-sz[v]+k); // 转化为路径经过几次问题 
                f[u][j]=max(f[u][j],f[u][j-k]+val+f[v][k]);   /// 相当于 把树节点当成一个背包,包套包,以包为单位 

            }
        }
    }
}


int main(){
    
    scanf("%d%d",&n,&T);
    for(ri i=1;i)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        edge t=edge(b,c);
        p[a].push_back(t);
        t=edge(a,c);
        p[b].push_back(t);
    }
    
    memset(f,-1,sizeof(f));
    
    dfs(1);
    
    printf("%lld",f[1][T]);
    return 0;    
}

相关