zoukankan      html  css  js  c++  java
  • 洛谷 3177 [HAOI2015] 树上染色

    题目描述

    有一棵点数为 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[i][j]表示以i为根的子树内有j个点为黑点时边的收益,从叶子到根的顺序计算每条边的收益,遍历到i点时,i的子树里的边已经被计算过了,i到其儿子的边被新加了进来,用这些新加的边的收益和子树的收益更新以i为根的子树的收益。

    转移方程为dp[i][j]=dp[k][l]+边权*子树内黑点*子树外黑点+边权*子树内白点*子树外白点。

    一开始我以为自己计算的是答案的两倍,这样智障了很久,后来把/2删掉后过了,我又仔细思考了一下,我是一条边一条边的算的,所以只会算一次。

    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #define nn 2010
    #define mm 4010
    #define lo long long
    #define inf -100000000
    using namespace std;
    int e=0;
    int fir[nn],nxt[mm],to[mm],w[mm],size[nn],n,k;
    lo dp[nn][nn];
    bool vis[nn]; 
    int get()
    {
        int ans=0,f=1;char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {ans=ans*10+ch-'0';ch=getchar();}
        return ans*f;
    }
    void add(int a,int b,int c)
    {
        nxt[++e]=fir[a];fir[a]=e;to[e]=b;w[e]=c;
        nxt[++e]=fir[b];fir[b]=e;to[e]=a;w[e]=c;
    }
    void dfs(int o)
    {
        size[o]=1;
        for(int i=fir[o];i;i=nxt[i])
          if(!vis[to[i]])
          {
              vis[to[i]]=1;
              dfs(to[i]);
              size[o]+=size[to[i]];
          }
    }
    void solve(int o)
    {
        dp[o][0]=0;
        dp[o][1]=0;
        for(int i=fir[o];i;i=nxt[i])
          if(size[o]>size[to[i]])
          {
              solve(to[i]);
              for(int j=size[o];j>=0;j--)                //把size[o]写成了size[i] 
                for(int p=0;p<=size[to[i]]&&p<=j;p++)
                  dp[o][j]=max(dp[o][j],dp[to[i]][p]+dp[o][j-p]+(lo)p*(k-p)*w[i]+(lo)w[i]*(size[to[i]]-p)*(n-k-size[to[i]]+p));
          }
    }
    int main()
    {
        n=get(),k=get();
        int a,b,c;
        for(int i=1;i<n;i++)
        {
            a=get();b=get();c=get();
            add(a,b,c);
        }
        vis[1]=1;
        dfs(1);
        for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
            dp[i][j]=inf;
        solve(1);
        printf("%lld",dp[1][k]);
        return 0;
    }
    

      

  • 相关阅读:
    C/C++通过WMI和系统API函数获取获取系统硬件配置信息
    C++ 获取mac地址
    解决Win7下UAC开启时无法响应WM_DROPFILES消息
    C++vector迭代器失效的问题
    解决Duilib创建的win32窗口拖到屏幕上边缘自动最大化
    VC++获取计算机Hardware Information (CPU ID, MainBoard Info, Hard Disk Serial, System Information)
    VC++实现在系统托盘来新消息闪烁,鼠标悬停显示窗口
    正则表达式的懒惰匹配?
    thinkphp-许愿墙-3
    thinkphp-许愿墙-2
  • 原文地址:https://www.cnblogs.com/charlotte-o/p/7501883.html
Copyright © 2011-2022 走看看