BZOJ4033[HAOI2015] 树上染色 解题报告【树上DP】

本文介绍了一种使用树形动态规划解决特定最短路径问题的方法,即在一个带权树中选择K个点染黑,其余染白,最大化黑点间及白点间的距离之和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。
问收益最大值是多少。
Input
第一行两个整数N,K。
接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。
输入保证所有点之间是联通的。
N<=2000,0<=K<=N
Output
输出一个正整数,表示收益的最大值。
Sample Input
5 2
1 2 3
1 5 1
2 3 1
2 4 2
Sample Output
17
【样例解释】
将点1,2染黑就能获得最大收益。
解题报告
我们考虑对于一条边ed[i],他的权值会对答案有什么影响?
我们令u=ed[i].u,v=ed[i].v,w=ed[i].w;
那么他的贡献就是 w*( size[u]中的黑点 * size[v]中的黑点 +size[u]中的白点 * size[v]中的白点)()。
我们令dp[u][j]表示以u为根节点的子树染j个黑点的最优答案,最终答案就在dp[1][k]中。
代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2000;
struct edge
{
    int v,next;
    long long w;
}ed[2*N+5];
int n,K;
int head[N+5],num;
long long dp[N+5][N+5],size[N+5],temp[N+5];
void build(int u,int v,long long w)
{
    ed[++num].v=v;
    ed[num].w=w;
    ed[num].next=head[u];
    head[u]=num;
}
void dfs(int u,int f)
{
    size[u]=1;
    for(int i=head[u];i!=-1;i=ed[i].next)
    {
        int v=ed[i].v;
        if(v==f)continue;
        dfs(v,u);
        memset(temp,0,sizeof(temp));
        for(int j=0;j<=size[u];j++)
        for(int k=0;k<=size[v];k++)
        temp[j+k]=max(temp[j+k],dp[u][j]+dp[v][k]+ed[i].w*(k*(K-k)+(size[v]-k)*(n-K-(size[v]-k))));
        size[u]+=size[v];
        for(int i=0;i<=size[u];i++)dp[u][i]=temp[i];
    }
}
int main()
{
    scanf("%d%d",&n,&K);
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n-1;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        build(u,v,w);
        build(v,u,w);
    }
    dfs(1,0);
    printf("%lld\n",dp[1][K]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值