zoukankan      html  css  js  c++  java
  • P3177 [HAOI2015]树上染色

    链接:https://www.luogu.org/problemnew/show/P3177

    题目描述

    有一棵点数为 N 的树,树边有边权。给你一个在 0~ N 之内的正整数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的N-K个点染成白色 。 将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。

    输入输出格式

    输入格式:

    第一行包含两个整数 N, K 。接下来 N-1 行每行三个正整数 fr, to, dis , 表示该树中存在一条长度为 dis 的边 (fr, to) 。输入保证所有点之间是联通的。

    输出格式:

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

    输入输出样例

    输入样例#1: 复制
    3 1
    1 2 1
    1 3 2
    输出样例#1: 复制
    3

    说明

    对于 100% 的数据, 0<=K<=N <=2000

    题解:摘自洛谷

    正确的状态应该是,dp[u][i]表示以u为跟的子树中,选择i个黑节点,对答案有多少贡献

    为什么是说“对答案有多少贡献呢”?

    主要是想到一点,即分别考虑每条边对答案的贡献

    即,边一侧的黑节点数*另一侧的黑节点数*边权+一侧的白节点数*另一侧的白节点数*边权

    这点很容易证明,但是不容易想到(原因是我太弱了)

    然后情况就明了了,整个问题成了一个树形背包,考虑每个子节点分配多少个黑色节点(体积),然后算出这条边对答案的贡献(价值)

    这里再一次强调“贡献”,是因为这个贡献不只是在当前子树内,而是对于整棵树来说的

    转移方程为dp[u][i] = max( dp[u][i], dp[u][i-j] + dp[v][j] + val )

    其中v为u的子节点,j为在这个子节点中选择的黑色点的个数,val为这条边的贡献

    val = j*(k-j)*w + (sz[v]-j)*(n-k+j-sz[v])*w

    其中w为这条边的边权,n为总的节点数,k为总的需要选择的黑色节点数,sz[v]为以v为根的子树的节点数量

    #include <bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int maxn = 2005;
    int h[maxn], siz[maxn], n, k, tot, now=0;
    LL dp[maxn][maxn];
    struct edge{
        int v, nxt;
        LL w;
    }G[maxn<<1];
    void add(int u, int v, LL w){
        G[++tot].v = v;
        G[tot].nxt = h[u];
        h[u] = tot;
        G[tot].w = w;
    }
    void dfs(int u, int f){
        siz[u] = 1;
        dp[u][0] = dp[u][1] = 0;
        for(int i = h[u]; i; i = G[i].nxt){
            int v = G[i].v;
            if(v == f)continue;
            dfs(v, u);
            siz[u] += siz[v];
        }
        for(int i = h[u]; i; i = G[i].nxt){
            int v = G[i].v;
            if(v == f)continue;
               for(int j = min(k, siz[u]); j >= 0; j--)
                  for(int kk = 0; kk <= min(j, siz[v]); kk++){
                     LL val =  1LL* G[i].w * ((k-kk)*kk + (n-k-siz[v]+kk)*(siz[v]-kk));
                     if(dp[u][j-kk] >=0)
                        dp[u][j] = max(dp[u][j], dp[u][j-kk] + dp[v][kk] + val);
            }
     
     
        }
     
     
    }
     
    int main()
    {
        scanf("%d%d", &n, &k);
        memset(dp, -1, sizeof(dp));
        for(int i = 1; i < n; i++){
            int u, v;
            LL w;
            scanf("%d%d%lld", &u, &v, &w);
            add(u, v, w);
            add(v, u, w);
        }
        dfs(1, -1);
        printf("%lld
    ", dp[1][k]);
        return 0;
    }
    View Code
     
  • 相关阅读:
    JAVA安装
    capture格式布局
    CSS样式表
    进制的转换
    CentOs7设置主机名称,以及主机名称和ip的对应关系
    CentOS7中NAT网卡设置静态IP
    CentOs7安装配置JDK
    基于Go语言构建区块链:part5
    基于Go语言构建区块链:part4
    BoltDB使用笔记
  • 原文地址:https://www.cnblogs.com/EdSheeran/p/9353225.html
Copyright © 2011-2022 走看看