BZOJ 4033[HAOI2015] 树上染色(树形DP)
4033: [HAOI2015]树上染色
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 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
1 2 3
1 5 1
2 3 1
2 4 2
Sample Output
17
【样例解释】
将点1,2染黑就能获得最大收益。
【样例解释】
将点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 }