【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
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; }