• bzoj 4033: [HAOI2015]树上染色


    4033: [HAOI2015]树上染色

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 1886  Solved: 805

    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染黑就能获得最大收益。
     
    题解:
    这个题目伤透了我的心,注意一定要全开long  long....
    好了,这个题目和树形背包十分类似,状态十分显然,我们设dp[i][j] 表示以i为节点的黑点个数是j的最大价值,怎么转移呢?这里十分巧妙,我们直接算是不可能的,那样就成了暴力,所以我们可以考虑每条边对整个答案的贡献,因为是两两点对,所以是子树中白点的数量乘以不在子树中的白点数量加上黑点的,所以状态转移就是dp[now][j+have]=max(dp[now][j+have],dp[now][have]+dp[to][j]+quan*j*(k-j)+(ll)(quan*(size[to]-j)*(n-k-size[to]+j)));后面的两个表示变对白点的贡献和边对黑点的贡献,然后我们跑一边01背包就可以了。最后不要忘记将子的结果和i节点合并。
     
     
    代码
    #include<iostream>
    #include<stdio.h>
    #include<stdlib.h>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    const int MAXN=3005;
    using namespace std;
    ll dp[MAXN][MAXN];
    ll size[MAXN];
    ll n,k,num=0;
    struct edge{
        ll first;
        ll next;
        ll to;
        ll quan;
    }a[MAXN*2];
     
    void addedge(ll from,ll to,ll quan){
        a[++num].to=to;
        a[num].quan=quan;
        a[num].next=a[from].first;
        a[from].first=num;
    }
     
    void dfs(ll now,ll f){
        size[now]=1;
        for(ll i=a[now].first;i;i=a[i].next){
            ll to=a[i].to;
            if(to==f) continue;
            dfs(to,now);
            size[now]+=size[to];
        }
      ll fill=1;
        for(ll i=a[now].first;i;i=a[i].next){
            ll to=a[i].to,quan=a[i].quan;
            if(to==f) continue;
            for(ll have=fill;have>=0;have--){
                for(ll j=min(size[to],k);j>=0;j--){
                    if(dp[to][j]>=0&&have+j<=k){
                        dp[now][j+have]=max(dp[now][j+have],dp[now][have]+dp[to][j]+quan*j*(k-j)+(ll)(quan*(size[to]-j)*(n-k-size[to]+j)));
                    }
                }
            }
            fill=min(k,fill+size[to]);
        }
    }
     
    int main(){
        scanf("%lld%lld",&n,&k);
        memset(dp,0,sizeof(dp));
        memset(size,0,sizeof(size));
        for(int i=1;i<n;i++){
            ll x,y,z;scanf("%lld%lld%lld",&x,&y,&z);
            addedge(x,y,z);
            addedge(y,x,z);
        }
        dfs(1,0);
        printf("%lld
    ",dp[1][k]);
    }
  • 相关阅读:
    keepalived排错
    shell脚本记录
    mysql(mariadb)主从配置
    添加硬盘设备
    天数
    centos7网络配置总结
    Keepalibed监控nginx
    zabbix安装
    基于JWT的web api身份验证及跨域调用
    C# webapi 权限验证
  • 原文地址:https://www.cnblogs.com/renjianshige/p/7327872.html
Copyright © 2020-2023  润新知