BZOJ 4033[HAOI2015] 树上染色(树形DP)

4033: [HAOI2015]树上染色

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 3188  Solved: 1366
[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

2017.9.12新加数据一组 By GXZlegend

题解

这题还是可以的。

我们用dp[i][j]代表i的子树中有j个黑点时子树中的边(如果不是整棵树的根节点也包括这个根节点和父亲的边)的最大贡献。

设这条边权为w,把整棵树分为x,y两部分 所以一条边的贡献为 边的长度*(x中的白点数*y中的白点数+x中的黑点数*y中的黑点数)

所以转化为了背包问题。每颗子树选不同的黑点有不同的权值。

然后最后加上当前子树根节点的贡献

 

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 const long long N=2010;
 8 long long n,k,cnt,head[N],size[N],dp[N][N*2];
 9 struct edge{
10     long long  to,nxt,w;
11 }e[N*2];
12 void add(long long u,long long v,long long w){
13     cnt++;
14     e[cnt].nxt=head[u];
15     e[cnt].to=v;
16     e[cnt].w=w;
17     head[u]=cnt;
18 }
19 void getdp(long long u,long long fa,long long w){
20     size[u]=1;
21     for(long long i=head[u];i;i=e[i].nxt){
22         long long v=e[i].to;
23         if(v==fa)continue;
24         getdp(v,u,e[i].w); 
25         for(long long j=min(k,size[u]);j>=0;j--)
26             for(long long z=min(size[v],k-j);z>=0;z--){
27                 dp[u][j+z]=max(dp[u][j+z],dp[u][j]+dp[v][z]);
28             }
29         size[u]+=size[v];
30     }
31     for(long long i=0;i<=min(k,size[u]);i++)dp[u][i]+=w*((size[u]-i)*(n-k-(size[u]-i))+i*(k-i));
32 }
33 int main(){
34     scanf("%lld%lld",&n,&k);
35     for(long long i=2;i<=n;i++){
36         long long u,v,w;
37         scanf("%lld%lld%lld",&u,&v,&w);
38         add(u,v,w);
39         add(v,u,w);
40     }
41     getdp(1,0,0);
42     printf("%lld",dp[1][k]);
43     return 0;
44 }

 

posted @ 2018-09-04 18:40  Xu-daxia  阅读(182)  评论(0编辑  收藏  举报