HAOI2015 树上染色

本文介绍了一种使用树形动态规划方法解决特定染色问题的算法。在一个带边权的树中,通过将k个点染成黑色,其余染成白色,旨在最大化黑点与白点间距离之和的收益。文章详细阐述了解决方案的思路,并提供了具体的代码实现。

一棵带边权的树,你需要把 $k$ 个点染成黑色,剩下的染成白色,你会获得黑点间两两间距离之和 + 白点间两两距离之和的收益

求收益最大值

$n \leq 5000$

sol:

树形 dp

显然一条边的贡献是 $边权 \times (左边白色 \times 右边白色 + 左边黑色 \times 右边黑色)$

$f_{(x,i)}$ 表示 $x$ 子树内选了 $i$ 个黑点,子树里的边对全局答案的贡献

然后我们按树上背包转移,转移的时候加的贡献其实是一条父子边的贡献

我们假设在 $v$ 点下面放了 $w$ 个黑点

这条边的贡献就是 $$边权 \times (w \times (k - w) + (size_v - k) \times (n - size_v - k + w))$$

 

还是很简单的

 

#include<bits/stdc++.h>
#define LL long long
#define int long long
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int inf = 1e16;
int n,m,nk;
struct EDG{int to,val;};
vector<EDG> G[2100];
int f[2100][2100],size[2100],tmp[2100];
inline void dfs(int x,int fa)
{
    size[x] = 1;
    for(int i=0;i<G[x].size();i++)
    {
        int to = G[x][i].to,len = G[x][i].val;
        if(to == fa)continue;
        dfs(to,x);
        fill(tmp,tmp+size[x]+size[to]+1,-inf);
        for (int j=0;j<=size[x];j++)
            for (int k=0;k<=size[to];k++)
                tmp[j+k]=max(tmp[j+k],f[x][j]+f[to][k]+(LL)len*((LL)k*(m-k)+(LL)(n-size[to]-m+k)*(size[to]-k)));
        size[x]+=size[to];
        for (int j=0;j<=size[x];j++) f[x][j]=tmp[j];
    }
}
signed main()
{
    n = read();m = read();
    nk = n - m;
    for(int i=2;i<=n;i++)
    {
        int u = read(),v = read(),w = read();
        G[u].push_back((EDG){v,w});
        G[v].push_back((EDG){u,w});
    }
    dfs(1,1);
    cout<<f[1][m];
}
View Code

 

转载于:https://www.cnblogs.com/Kong-Ruo/p/9891088.html

帮我调试下面代码: 代码如下:#include<bits/stdc++.h> using namespace std; const int MAXN = 2005; int N, K; int head[MAXN], cnt; struct star{ int nxt, to, w; }edge[MAXN * 2]; void add( int u, int v, int w ){ edge[++ cnt] = { head[u], v, w }; head[u] = cnt; } int f[MAXN][MAXN], siz[MAXN];//f[i][j]表示以i为根的子树中有j个黑点的收益最大值 void dfs( int u, int fa ){ siz[u] = 1; f[u][0] = f[u][1] = 0; for( int i = head[u]; i; i = edge[i].nxt ){ int to = edge[i].to; if( to == fa ) continue; dfs( to, u ); siz[u] += siz[to]; for( int j = min( K, siz[u] ); j >= 0; j -- ){ if( f[u][j] != -1 ) f[u][j] += f[to][0] + siz[to] * ( N - K - siz[to] ) * edge[i].w; for( int l = min( j, siz[to] ); l >= 0; l -- ){ if( f[u][j - l] == -1 ) continue; int cost = ( l * ( K - l ) + ( siz[to] - l ) * ( N - K - siz[to] + l ) ) * edge[i].w; f[u][j] = max( f[u][j], f[u][j - l] + f[u][l] + cost ); } } } } int main(){ cin >> N >> K; memset( f, -1, sizeof f ); for( int i = 1; i < N; i ++ ){ int u, v, w; cin >> u >> v >> w; add( u, v, w ); add( v, u, w ); } dfs( 1, 0 ); cout << f[1][K]; return 0; } 题面如下:# P3177 [HAOI2015] 树上染色 ## 题目描述 有一棵点数为 $n$ 的树,树边有边权。给你一个在 $0 \sim n$ 之内的正整数 $k$ ,你要在这棵树中选择 $k$ 个点,将其染成黑色,并将其他的 $n-k$ 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的收益。问收益最大值是多少。 ## 输入格式 第一行包含两个整数 $n,k$。 第二到 $n$ 行每行三个正整数 $u, v, w$,表示该树中存在一条长度为 $w$ 的边 $(u, v)$。输入保证所有点之间是联通的。 ## 输出格式 输出一个正整数,表示收益的最大值。 ## 输入输出样例 #1 ### 输入 #1 ``` 3 1 1 2 1 1 3 2 ``` ### 输出 #1 ``` 3 ``` ## 说明/提示 对于 $100\%$ 的数据,$0 \leq n,k \leq 2000$。
最新发布
10-01
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值