• 博客园logo
  • 会员
  • 周边
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
MZQ667
博客园    首页    新随笔    联系   管理    订阅  订阅

[Luogu] P2015 二叉苹果树

[Luogu] P2015 二叉苹果树


1.题目

题目描述

有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)
这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树

2   5
\ / 
 3   4
  \ /
   1

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。\

输入输出格式

输入格式:

第1行2个数,N和Q(1<=Q<= N,1<N<=100)。
N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。
每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。
每根树枝上的苹果不超过30000个。

输出格式:

一个数,最多能留住的苹果的数量。

输入输出样例

输入样例#1:

5 2
1 3 1
1 4 10
2 3 20
3 5 20

输出样例#1:

21

2.题解

树形DP。

dfs,然后对各节点边统计子树边DP。

设\(f[i][j]\)为当前为第\(i\)个点,在其子树上选择了\(j\)条边后的最大边权和,\(size[i]\)为以\(i\)为根节点的子树上的边数。对当前点跑一个背包。

转移方程为\(f[i][j] = \max(f[i][j], f[i][j - k - 1] + f[v][k] + w)\),其中\(1 \leqslant j \leqslant \min(size[i], m)\),\(v \in i's~subtree\),\(0 \leqslant k \leqslant \min(j - 1, size[v])\),\(w\)为当前连接\(i\)与\(v\)的边权。

\(j, v\)的取值都比较好理解,\(k \geqslant 0\)的原因是可以只选当前边,不考虑子树。\(\leqslant j - 1\)的原因是需要为当前边预留位置。

#include <cstdio>
const int MAXN = 110;
struct EDGE{
    int to, next, wgh;
}edge[MAXN << 1];
int n, m, top, u, v, w;
int f[MAXN][MAXN], size[MAXN], head[MAXN];
template <class T>
inline T min(T a, T b) {return (a < b ? a : b);}
template <class T>
inline T max(T a, T b) {return (a > b ? a : b);}
inline void add_edge(int u, int v, int w) {
    edge[++top].to = v;
    edge[top].wgh = w;
    edge[top].next = head[u];
    head[u] = top;

    edge[++top].to = u;
    edge[top].wgh = w;
    edge[top].next = head[v];
    head[v] = top;
    
    return ;
}
inline void dfs(int now, int fa) {
    int tmp(0), tmw(0);
    for (int i = head[now]; i; i = edge[i].next)
        if((tmp = edge[i].to) != fa) {
            dfs(tmp, now); size[now] += size[tmp] + 1;
            tmw = edge[i].wgh;
            for (int j = min(size[now], m); j >= 1; --j)
                for (int k = min(j - 1, size[tmp]); ~k; --k)
                    f[now][j] = max(f[now][j], f[now][j - k - 1] + f[tmp][k] + tmw);
        }
    return ;
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i < n; ++i)
        scanf("%d%d%d", &u, &v, &w), add_edge(u, v, w);
    dfs(1, 1);
    printf("%d\n", f[1][m]);
    return 0;
}
知识共享许可协议
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
posted @ 2018-10-16 09:55  mzq667  阅读(251)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3