BZOJ4987:Tree(树形DP)

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

Solution

贴一下神仙$beginend$的题解qwq
显然最优情况一定是原树的一棵大小为k的连通子树,然后直径上的边系数是1,其余边的系数是2。
那么我们可以进行树形dp,设f[i,j,0/1/2]表示以i为根的子树选了j个点,且直径的端点已经选了0/1/2个的最优方案。

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #define N (3009)
 5 using namespace std;
 6 
 7 struct Edge{int to,next,len;}edge[N<<1];
 8 int n,m,u,v,l,ans,INF,size[N],f[N][N][3],tmp[N][3];
 9 int head[N],num_edge;
10 
11 void add(int u,int v,int l)
12 {
13     edge[++num_edge].to=v;
14     edge[num_edge].len=l;
15     edge[num_edge].next=head[u];
16     head[u]=num_edge;
17 }
18 
19 void Min(int &x,int y){x=min(x,y);}
20 
21 void Dfs(int x,int fa)
22 {
23     size[x]=1;
24     for (int i=0;i<=n;i++)
25         for (int j=0;j<=2;j++)
26             f[x][i][j]=INF;
27     f[x][1][0]=f[x][1][1]=f[x][1][2]=0;
28     for (int i=head[x]; i; i=edge[i].next)
29         if (edge[i].to!=fa)
30         {
31             Dfs(edge[i].to,x);
32             for (int j=0; j<=size[x]+size[edge[i].to]; ++j)
33                 tmp[j][0]=tmp[j][1]=tmp[j][2]=INF;
34             for (int j=0; j<=size[x]; ++j)
35                 for (int k=0; k<=size[edge[i].to]; ++k)
36                 {
37                     int to=edge[i].to,len=edge[i].len;
38                     Min(tmp[j+k][0],f[x][j][0]+f[to][k][0]+len*2);
39                     Min(tmp[j+k][1],f[x][j][1]+f[to][k][0]+len*2);
40                     Min(tmp[j+k][1],f[x][j][0]+f[to][k][1]+len);
41                     Min(tmp[j+k][2],f[x][j][1]+f[to][k][1]+len);
42                     Min(tmp[j+k][2],f[x][j][0]+f[to][k][2]+len*2);
43                     Min(tmp[j+k][2],f[x][j][2]+f[to][k][0]+len*2);
44                 }
45             size[x]+=size[edge[i].to];
46             for (int j=0; j<=size[x]; ++j)
47                 for (int k=0; k<=2; ++k)
48                     f[x][j][k]=min(f[x][j][k],tmp[j][k]);
49         }
50     ans=min(ans,f[x][m][2]);
51 }
52 
53 int main()
54 {
55     memset(&INF,0x3f,sizeof(INF));
56     scanf("%d%d",&n,&m);
57     for (int i=1; i<=n-1; ++i)
58     {
59         scanf("%d%d%d",&u,&v,&l);
60         add(u,v,l); add(v,u,l);
61     }
62     ans=INF; Dfs(1,0);
63     printf("%d\n",ans);
64 }

转载于:https://www.cnblogs.com/refun/p/9774584.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值