【Luogu】P3177树上染色(树形DP)

本文介绍了一种基于树形背包的算法实现思路,通过计算树中每条边的贡献值来解决特定问题。该算法利用了动态规划的思想,并详细展示了如何通过遍历树结构并考虑黑白节点数量来优化解决方案。

  题目链接

  题没想出来很烦+一堆细节要注意很烦。

  当然更可能是我智商被osu吃了。

  考虑一条边会有什么贡献?它一边的黑点数*另一边的黑点数*边权。

  +它一边的白点数*另一边的白点数*边权。

  这样一来就成了一个树形背包

  

#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#define maxn 2030
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

struct Edge{
    long long next,to,dis;
}edge[maxn*3];
long long head[maxn],num;
inline void add(long long from,long long to,long long dis){
    edge[++num]=(Edge){head[from],to,dis};
    head[from]=num;
}

long long n,m;
long long size[maxn];
long long f[maxn][maxn];

void build(long long x,long long fa){
    size[x]=1;
    for(long long i=head[x];i;i=edge[i].next){
        long long to=edge[i].to;
        if(to==fa)    continue;
        build(to,x);
        size[x]+=size[to];
    }
}

inline long long calc(long long i,long long x){
    return (m-x)*x*edge[i].dis+(n-m+x-size[edge[i].to])*(size[edge[i].to]-x)*edge[i].dis;
}

void dfs(long long x,long long fa){
    memset(f[x],-1,sizeof(f[x]));    f[x][0]=f[x][1]=0;
    for(long long i=head[x];i;i=edge[i].next){
        long long to=edge[i].to;
        if(to==fa)    continue;
        dfs(to,x);
        for(long long j=min(m,size[x]);j>=0;--j){
            for(long long k=0;k<=min(j,size[to]);++k)
                if(f[x][j-k]!=-1)    f[x][j]=max(f[x][j],f[x][j-k]+f[to][k]+calc(i,k));
        }
    }
    return;
}

int main(){
    n=read(),m=read();
    for(long long i=1;i<n;++i){
        long long from=read(),to=read(),dis=read();
        add(from,to,dis);
        add(to,from,dis);
    }
    build(1,1);
    dfs(1,1);
    printf("%lld\n",f[1][m]);
    return 0;
}

 

转载于:https://www.cnblogs.com/cellular-automaton/p/8342623.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值