【BZOJ4987】Tree 树形DP

【BZOJ4987】Tree

Description

从前有棵树。
找出K个点A1,A2,…,Ak。
使得∑dis(AiAi+1),(1<=i<=K-1)最小。

Input

第一行两个正整数n,k,表示数的顶点数和需要选出的点个数。
接下来n-l行每行3个非负整数x,y,z,表示从存在一条从x到y权值为z的边。
I<=k<=n。
l<x,y<=n
1<=z<=10^5
n <= 3000

Output

一行一个整数,表示最小的距离和。

Sample Input

10 7
1 2 35129
2 3 42976
3 4 24497
2 5 83165
1 6 4748
5 7 38311
4 8 70052
3 9 3561
8 10 80238

Sample Output

184524

题解:用f[i][j][0/1/2]表示在i的子树中,选择j个点,且包含0/1/2个路径的端点,的最小总长度。然后跑树形背包即可。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,m,cnt,ans;
int f[3010][3010][3],to[6010],next[6010],val[6010],head[3010],siz[3010];
inline void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
void dfs(int x,int fa)
{
	int i,j,k,l,h,y;
	f[x][1][0]=f[x][1][1]=f[x][1][2]=0,siz[x]=1;
	for(i=head[x];i!=-1;i=next[i])	if(to[i]!=fa)
	{
		dfs(to[i],x),y=to[i];
		for(k=min(siz[x],m);k;k--)	for(l=0;l<=2;l++)
			for(j=min(siz[y],m-k);j;j--)	for(h=0;h<=2-l;h++)
				f[x][k+j][l+h]=min(f[x][k+j][l+h],f[x][k][l]+f[y][j][h]+val[i]+val[i]*(h!=1));
		siz[x]+=siz[y];
	}
	ans=min(ans,f[x][m][2]);
}
int main()
{
	n=rd(),m=rd();
	int i,a,b,c;
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)	a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c);
	memset(f,0x3f,sizeof(f));
	ans=1<<30;
	dfs(1,0);
	printf("%d",ans);
	return 0;
}
posted @ 2017-09-20 11:24  CQzhangyu  阅读(294)  评论(0编辑  收藏