### 题面
https://www.lydsy.com/JudgeOnline/problem.php?id=4987
### 分析

先考虑贪心,求出树的直径,显然直径应该只被经过1次(最长的边应该走最少次数),其他非直径上的边被经过2次,整体的形状应该类似一条链上接着许多子树
考虑树形DP
子状态:$dp[x][i][j]( j\in \left\{0,1,2\right\})$,表示以x为根的子树中选了i个点,i个点中有j个直径的端点时的长度之和
其中x为树上一点,x为y的父亲,j为x子树去掉y子树后子树内选的点的个数,k为y子树内选的点的个数.接下来开始毒瘤的分类讨论
1. x,y的子树均不包含直径,边(x,y)属于子树内部的边,被算了2次$$dp[x][j+k][0]=\min(dp[x][j+k][0],dp[x][j][0]+dp[y][k][0]+2\cdot len);$$
2. y的子树包含直径的1个端点,则直径一定经过y,边(x,y)在直径上只被算1次$$dp[x][j+k][1]=\min(dp[x][j+k][1],dp[x][j][0]+dp[y][k][1]+len);$$
3. x的子树包含直径的1个端点,y的子树不包含直径的端点,通过边(x,y)与直径相连,被算2次$$dp[x][j+k][1]=\min(dp[x][j+k][1],dp[x][j][1]+dp[y][k][0]+2\cdot len);$$
4. x的子树包含直径的2个端点,y的子树不包含直径的端点,通过边(x,y)与直径相连,被算2次$$dp[x][j+k][2]=\min(dp[x][j+k][2],dp[x][j][2]+dp[y][k][0]+2\cdot len);$$
5. y的子树包含直径的2个端点,则直径一定不经过边(x,y),边(x,y)被算2次$$dp[x][j+k][2]=\min(dp[x][j+k][2],dp[x][j][0]+dp[y][k][2]+2\cdot len);$$
6. x,y的子树各包含直径的1个端点,直径一定过(x,y),边(x,y)被算1次$$dp[x][j+k][2]=\min(dp[x][j+k][2],dp[x][j][1]+dp[y][k][1]+len);$$

### 代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 3005
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int qread(){
int x=0,sign=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*sign;
} int n,m;
struct edge {
int from;
int to;
int next;
long long len;
edge() { }
edge(int u,int v,long long w,int hd) {
from=u;
to=v;
next=hd;
len=w;
}
} E[maxn<<1];
int head[maxn];
int ecnt=0;
void add_edge(int u,int v,long long w) {
E[++ecnt]=edge(u,v,w,head[u]);
head[u]=ecnt;
} int sz[maxn];
long long dp[maxn][maxn][3];
void dfs(int x,int fa) {
sz[x]=1;
dp[x][1][0]=dp[x][1][1]=0;
for(int i=head[x]; i; i=E[i].next) {
int y=E[i].to;
long long len=E[i].len;
if(y!=fa) {
dfs(y,x);
//j类似01背包中的第二层,为防止被算多次,倒序循环
for(int j=min(sz[x],m); j>=0; j--) {//j为x子树去掉y子树后子树内选的点的个数
for(int k=min(sz[y],m-j); k>=0; k--) {//k为y子树内选的点的个数
dp[x][j+k][0]=min(dp[x][j+k][0],dp[x][j][0]+dp[y][k][0]+len*2);
//x,y的子树均不包含直径,边(x,y)属于子树内部的边,被算了2次
dp[x][j+k][1]=min(dp[x][j+k][1],dp[x][j][0]+dp[y][k][1]+len);
//y的子树包含直径的1个端点,则直径一定经过y,边(x,y)在直径上只被算1次
dp[x][j+k][1]=min(dp[x][j+k][1],dp[x][j][1]+dp[y][k][0]+len*2);
//x的子树包含直径的1个端点,y的子树不包含直径的端点,通过边(x,y)与直径相连,被算2次
dp[x][j+k][2]=min(dp[x][j+k][2],dp[x][j][2]+dp[y][k][0]+len*2);
//x的子树包含直径的2个端点,y的子树不包含直径的端点,通过边(x,y)与直径相连,被算2次
dp[x][j+k][2]=min(dp[x][j+k][2],dp[x][j][0]+dp[y][k][2]+len*2);
//y的子树包含直径的2个端点,则直径一定不经过边(x,y),边(x,y)被算2次
dp[x][j+k][2]=min(dp[x][j+k][2],dp[x][j][1]+dp[y][k][1]+len);
//x,y的子树各包含直径的1个端点,直径一定过(x,y),边(x,y)被算1次
}
}
sz[x]+=sz[y];
}
}
}
int main() {
// freopen("tree9.in","r",stdin);
int u,v;
long long w;
n=qread();
m=qread();
for(int i=1;i<n;i++){
u=qread();
v=qread();
w=qread();
add_edge(u,v,w);
add_edge(v,u,w);
}
memset(dp,0x3f,sizeof(dp));
dfs(1,0);
long long ans=INF;
for(int i=1;i<=n;i++){
ans=min(ans,dp[i][m][2]);//根据之前贪心的分析,直径一定经过这k个点中的一些点
}
cout<<ans;
}

BZOJ 4987 (树形DP)的更多相关文章

  1. BZOJ 1040 树形DP+环套树

    就是有n个点n条边,那么有且只有一个环那么用Dfs把在环上的两个点找到.然后拆开,从这条个点分别作树形Dp即可. #include <cstdio> #include <cstrin ...

  2. BZOJ - 2500 树形DP乱搞

    题意:给出一棵树,两个给给的人在第\(i\)天会从节点\(i\)沿着最长路径走,求最长的连续天数\([L,R]\)使得\([L,R]\)为起点的最长路径极差不超过m 求\(1\)到\(n\)的最长路经 ...

  3. BZOJ 4033 树形DP

    http://blog.csdn.net/mirrorgray/article/details/51123741 安利队长blog- 树形dp吧,状态挺显然的,dp[x][j]表示以x为根的子树中,选 ...

  4. [USACO10MAR]伟大的奶牛聚集 BZOJ 1827 树形dp+dfs

    题目描述 Bessie is planning the annual Great Cow Gathering for cows all across the country and, of cours ...

  5. bzoj 4007 树形dp

    题目大意 脸哥最近来到了一个神奇的王国,王国里的公民每个公民有两个下属或者没有下属,这种关系刚好组成一个 n 层的完全二叉树.公民 i 的下属是 2 * i 和 2 * i +1.最下层的公民即叶子节 ...

  6. BZOJ 1369 树形DP

    思路: f[i][j] 表示节点i 染成j时 子树的最小权值 (我会猜这个j很小 你打我吖~) 随便DP一发就好了 (证明我也不会) //By SiriusRen #include <cstdi ...

  7. [BZOJ 4033] [HAOI2015] T1 【树形DP】

    题目链接:BZOJ - 4033 题目分析 使用树形DP,用 f[i][j] 表示在以 i 为根的子树,有 j 个黑点的最大权值. 这个权值指的是,这个子树内部的点对间距离的贡献,以及 i 和 Fat ...

  8. [BZOJ 1907] 树的路径覆盖 【树形DP】

    题目链接:BZOJ - 1907 题目分析 使用树形 DP,f[x][0] 表示以 x 为根的子树不能与 x 的父亲连接的最小路径数(即 x 是一个折线的拐点). f[x][1] 表示以 x 为根的子 ...

  9. bzoj 4871: [Shoi2017]摧毁“树状图” [树形DP]

    4871: [Shoi2017]摧毁"树状图" 题意:一颗无向树,选两条边不重复的路径,删去选择的点和路径剩下一些cc,求最多cc数. update 5.1 : 刚刚发现bzoj上 ...

随机推荐

  1. linux服务器安全配置攻略

    引言: 最小的权限+最少的服务=最大的安全 所以,无论是配置任何服务器,我们都必须把不用的服务关闭.把系统权限设置到最小话,这样才能保证服务器最大的安全.下面是CentOS服务器安全设置,供大家参考. ...

  2. Linux服务器调优

    Linux内核参数 http://space.itpub.net/17283404/viewspace-694350 net.ipv4.tcp_syncookies = 表示开启SYN Cookies ...

  3. Python---Tkinter---贪吃蛇(稳定的外部环境,稳定的内心)

    # 项目分析: - 构成: - 蛇 Snake - 食物 Food - 世界 World - 蛇和食物属于整个世界 class World: self.snake self.food - 上面代码不太 ...

  4. Spring Cloud(1)相关概念

    单点系统架构 传统项目架构 传统项目分为三层架构,将业务逻辑层.数据库访问层.控制层放入在一个项目中. 优点:适合于个人或者小团队开发,不适合大团队开发. 分布式项目架构 根据业务需求进行拆分成N个子 ...

  5. 【leetcode】837. New 21 Game

    题目如下: 解题思路:这个题目有点像爬楼梯问题,只不过楼梯问题要求的计算多少种爬的方式,但是本题是计算概率.因为点数超过或者等于K后就不允许再增加新的点数了,因此我们可以确定最终Alice拥有的点数的 ...

  6. Quick BI的宝藏工具——交叉表

    对于普通的表格展示数据,相信大家都非常熟悉了,今天给大家介绍的是BI领域的分析利器-交叉表,这个在BI分析场景中使用占比最多的分析利器.通过交叉表对数据的承载和管理,用户可以一目了然地分析出各种场景指 ...

  7. Laya 使list渲染支持分帧的思路

    Laya 使list渲染支持分帧的思路 @author ixenos 2019-09-06 1.由于Laya的list渲染时没有做分帧处理,只做了延迟帧处理,所以当单页元素较多时,会有大量运算卡帧的情 ...

  8. Kohana Cache

    The default cache group is loaded based on the Cache::$default setting. It is set to the file driver ...

  9. 【PowerOJ1738&网络流24题】最小路径覆盖问题 (最大流)

    题意: 思路: [问题分析] 有向无环图最小路径覆盖,可以转化成二分图最大匹配问题,从而用最大流解决. [建模方法] 构造二分图,把原图每个顶点i拆分成二分图X,Y集合中的两个顶点Xi和Yi.对于原图 ...

  10. RabbitMQ消费端ACK与重回队列机制,TTL,死信队列详解(十一)

    消费端的手工ACK和NACK 消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿. 如果由于服务器宕机等严重问题,那么我们就需要手工进行ACK保障消费端成功. 消费端重回队列 ...