zoukankan      html  css  js  c++  java
  • bzoj4033

    4033: [HAOI2015]树上染色

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 1598  Solved: 667
    [Submit][Status][Discuss]

    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染黑就能获得最大收益。

    HINT

     

     

    Source

    这道题搞了很长时间(弱爆了)
    设计dp状态的时候有一个很烦人的问题,就是怎么考虑树外的点的贡献。(贡献自己脑补是什么)
    碰见这种题目,我们应该这么想:既然算不了树外的,那么我们把树内能算的东西都算了。
    原始思想:dp(i,j)以i为根子树内的答案的最大值,但是一旦我们想转移就gg了,于是我们把子树内的点的贡献也算上。
    怎么算呢?程序里写了,w是i的父亲到i的权值,我们把子树里所有点经过这条边的贡献算上就行了。
    temp每次都要清空,因为每次计算一个新的子树,答案要清空 temp保存当前这颗子树选了j个黑点的自己的最大贡献,最后再加上向外面的贡献
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2010;
    struct edge {
        int nxt, to, w;
    } e[N << 1];
    int n, m, cnt = 1;
    int head[N],  size[N];
    ll dp[N][N], temp[N];
    void link(int u, int v, int w)
    {
        e[++cnt].nxt = head[u];
        head[u] = cnt;
        e[cnt].to = v;
        e[cnt].w = w;
    }
    void dfs(int u, int last, int w)
    {
        size[u] = 1;
        memset(temp, 0, sizeof(temp));
        for(int i = head[u]; i ; i = e[i].nxt) if(e[i].to != last)
        {
            dfs(e[i].to, u, e[i].w);
            for(int j = 0; j <= size[u]; ++j) temp[j] = dp[u][j];
            for(int j = 0; j <= min(size[e[i].to], m); ++j)
                for(int k = 0; k <= min(size[u], m); ++k)
                    temp[j + k] = max(temp[j + k], dp[u][k] + dp[e[i].to][j]);            
            size[u] += size[e[i].to];
            for(int j = 0; j <= size[u]; ++j) dp[u][j] = max(temp[j], dp[u][j]);
        }        
        for(int j = 0; j <= min(size[u], m); ++j) 
            dp[u][j] = temp[j] + ((ll)j*(ll)(m-j)+(ll)(size[u]-j)*(ll)(n-m-size[u]+j))*(ll)w;        
    }
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i < n; ++i)
        {
            int u, v, w; scanf("%d%d%d", &u, &v, &w);
            link(u, v, w); link(v, u, w);
        }
        dfs(1, 0, 0);
        printf("%lld
    ", dp[1][m]);
        return 0;
    }
    View Code
  • 相关阅读:
    此类目的是防治序列化Json字符串时的循环引用问题-------最好解决方案
    Json.Net学习笔记
    深入理解javascript原型和闭包(完结)
    SDL 威胁建模工具入门 threat modeling tool
    .NET 4.0 中的契约式编程
    MVC调用部分视图PartialView
    visual studio 常识
    【阿里云产品评测】小鸡咕咕的初体验
    【阿里云入门产品免费试用半年】加入微博话题+“最”炫推荐理由,得精美小礼物
    镜像公测招募啦!!!用镜像开通云服务器,限时免费体验!!
  • 原文地址:https://www.cnblogs.com/19992147orz/p/6571945.html
Copyright © 2011-2022 走看看