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

    题目描述

    题目链接

    有一棵点数为 (N) 的树,树边有边权。

    给你一个在 (0 sim N) 之内的正整数 (K),你要在这棵树中选择 (K) 个点,将其染成黑色,并将其他的 (N-K) 个点染成白色。

    将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。

    问收益最大值是多少!

    输入格式

    第一行两个整数 (N,K)

    接下来 (N-1) 行每行三个正整数 (fr,to,dis),表示该树中存在一条长度为 (dis) 的边 ((fr,to))

    输入保证所有点之间是联通的。

    输出格式

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

    数据范围

    (1 le N le 2000),
    (0 le K le N)

    输入样例:

    5 2
    1 2 3
    1 5 1
    2 3 1
    2 4 2
    

    输出样例:

    17
    

    样例解释

    将点 (1,2) 染黑就能获得最大收益。

    解题报告

    题意理解

    原题很清晰,没法简析了。

    探索算法

    有一说一,第一眼看到这道题目,博主是真的没有认为这道题目的是一个树形DP。

    但是,染色的题目,加上最值问题的要求,还是和树形DP联系很大。

    那么我们为什么很难看出来呢?可能是因为我太菜了

    因为我们的关注点,是一个点,它所提供的贡献。

    而不是一条边,所提供的贡献

    其实这也是一个难想&常见的思路。

    对于树上的题目,求贡献,不是点,就是边至少提高组范畴是这样的

    那么如果说,我们按照边来思考的话,这道题目的转移方程还是很有意思的。

    算法解析

    类似于状态压缩动态规划,树形DP的题目,状态设计是极为重要的一步。

    那么如何设计状态呢?

    既然上面,我们说了,要按照边来算贡献,那么我们就先来分析边,是如何提供贡献的?

    对于一条边而言,他可以提供的贡献,来自于两个节点之间的路径有它,也就是经过它:

    1. 左右两边黑色节点提供的。(自己子树内的黑色节点 $ imes $ 子树外黑色节点。
    2. 左右两边白色节点提供的。(自己子树内的白色节点 $ imes $ 子树外白色节点。

    那么此时我们需要确定状态了

    ( f[x][i] 表示节点x它到父亲节点的这条边,他的子树有i个染黑节点,此时提供的最大贡献 )

    那么对于一个节点他有多少个染黑节点呢?

    不知道?

    那么枚举吧。

    于是本题成了一个树形背包。

    每个节点他的子树有多少个黑色节点(体积),然后算出这条边对答案的贡献(价值)

    [black\_sum=s*(k-s) \\ white\_sum=(Size[y]-s)*((n-Size[y])-(k-s)) \\ s为当前节点他的子树有多少个染成黑色的节点 \\ k为题目给出的有k个节点染成黑色 \\ Size[y]表示当前节点他的子树一共有多少个节点 ]

    因此本题就这么愉快的解决了,在这里默认,树形背包DP大家都会吧。。。

    如果不会的话,可以参照Acwing我曾经的背包DP的讲义。

    参考代码

    //My English is poor.The Code maybe have some grammer problems.
    //To have a better Font display in Acwing.com,I must choose English.
    #include <bits/stdc++.h>
    using namespace std;
    const int N=2100;
    int n,k,head[N<<1],edge[N<<1],Next[N<<1],tot,Size[N];
    long long f[N][N],ver[N<<1];
    inline void add_edge(int a,int b,int c)
    {
        edge[++tot]=b;
        ver[tot]=c;
        Next[tot]=head[a];
        head[a]=tot;
    }
    void tree_DP(int x,int fa)
    {
        Size[x]=1;
        memset(f[x],-1,sizeof(f[x]));
        f[x][0]=f[x][1]=0;
        for(int i=head[x]; i; i=Next[i])//visit its sons
        {
            int y=edge[i];
            if (y==fa)//its father
                continue;//can't visit its father node
            tree_DP(y,x);//visit the son
            Size[x]+=Size[y];//count the Size of the tree
        }
        //Next part is the DP whose kind is knapsack.
        for(int i=head[x]; i; i=Next[i])
        {
            int y=edge[i];
            if (y==fa)//its father
                continue;//can't visit its father node
            for(int j=min(k,Size[x]); j>=0; j--)//choose black nodes whose number is j
            {
                for(int s=0; s<=min(j,Size[y]); s++)
                    if (~f[x][j-s])
                    {
                        long long black_sum=s*(k-s);//balck pairs
                        long long white_sum=(Size[y]-s)*((n-Size[y])-(k-s));//white pairs
                        f[x][j]=max(f[x][j],f[x][j-s]+f[y][s]+(black_sum+white_sum)*ver[i]);
                    }
            }
        }
    }
    inline void init()
    {
        scanf("%d%d",&n,&k);
        for(int i=1; i<n; i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add_edge(a,b,c);
            add_edge(b,a,c);
        }
        tree_DP(1,0);
        printf("%lld
    ",f[1][k]);
    }
    signed main()
    {
        init();
        return 0;
    }
    
  • 相关阅读:
    oracle11g windows客户端安装
    leetcode做题总结
    后端工程师都应该知道的最佳实践
    阿里云ubuntu下nginx部署上线后报错问题
    漫谈逆向工程
    RPC远程过程调用(Remote Procedure Call)
    stub和proxy使用初衷
    vue如何绑定多个class style
    简单又好看的背景 , 代码实现方格背景
    使用C#.NET调用ICU进行编码检测和编码转换
  • 原文地址:https://www.cnblogs.com/gzh-red/p/14207802.html
Copyright © 2011-2022 走看看