BZOJ_4033_[HAOI2015]树上染色_树形DP

BZOJ_4033_[HAOI2015]树上染色_树形DP

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


树形背包,设f[i][j]表示i子树里选了j个黑点的最大收益。

转移时考虑父亲到儿子的这条边两端分别有多少个黑-黑  白-白统计一下贡献即可。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
#define N 2050
int K,head[N],to[N<<1],nxt[N<<1],val[N<<1],cnt,n;
int siz[N];
ll f[N][N];
inline void add(int u,int v,int w) {
	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w;
}
void dfs(int x,int y) {
	int i,j,k,flg=1; siz[x]=1;
	for(i=head[x];i;i=nxt[i]) {
		if(to[i]!=y) {
			flg=0;
			dfs(to[i],x);
			for(j=siz[x];j>=0;j--) {
				for(k=siz[to[i]];k>=0;k--) {
					f[x][j+k]=max(f[x][j+k],f[x][j]+f[to[i]][k]+val[i]*(ll(k)*(K-k)+ll(siz[to[i]]-k)*(n-K-siz[to[i]]+k)));
				}
			}
			siz[x]+=siz[to[i]];
		}
	}
	if(flg) f[x][0]=f[x][1]=0;
}
int main() {
	// memset(f,0x80,sizeof(f));
	scanf("%d%d",&n,&K);
	int i,x,y,z;
	for(i=1;i<n;i++) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
	dfs(1,0);
	printf("%lld\n",f[1][K]);
}

猜你喜欢

转载自www.cnblogs.com/suika/p/9464301.html