[Luogu2015] 二叉苹果树 [树形dp]

[ L i n k \frak{Link} ]


感觉自己在理清码代码思路上面还是有很大的不足
用树形dp练习一下。


我就卜把这题当作二叉树,直接套背包啦
f(x, c),表示以x为根的子树保留c条枝最多能够留下多少苹果。
那么,分类讨论:
对于某个点把它保留的c条枝头分别分给它的每个子节点
也就是说让每个子节点用一定的价值给父亲一定的贡献。
价值怎么分配?? 这个问题有一点点模糊,不过它实际上是一个很熟悉的模型
相当于某个子节点可以任取代价,某个代价可以得到某种价值
这是多重背包的经典模型。


进一步考虑,化为直观的状态和方程。
题目要求是选一个点子树中的点那么这个点必须没有被剪
于是f(x,c) = max{f(x,k)+Σf(childi,xi)+val(edge)}, Σxi=c-k-1
至于不保留这个点的情况就是f(x,0),会被父亲考虑到
因为根节点一定会被选(剪不掉),所以这样设计状态正好合适。


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<cctype>
#include<ctime>
using namespace std;
#define add_edge(a, b, c) nxt[++tot] = head[a], head[a] = tot, to[tot] = b, val[tot] = c
int n, q, tot;
int head[105], nxt[205], to[205], val[205], siz[105];
int f[105][105];
void dfs(int x) {
    siz[x] = 1;
    for (int i = head[x]; i; i = nxt[i]) {
        dfs(to[i]);
        siz[x] += siz[to[i]];
    }
    for (int i = head[x]; i; i = nxt[i]) {
        for (int k = siz[x] - 1; k >= 0; --k) {
            for (int j = 0; j < siz[to[i]]; ++j) {
                if (j >= k) continue;
                f[x][k] = max(f[x][k], f[x][k-j-1] + f[to[i]][j] + val[i]);
            }
        }
    }
}
int main() {
    scanf("%d%d", &n, &q);
    for (int a, b, c, i = 1; i < n; ++i) {
        scanf("%d%d%d", &a, &b, &c);
        add_edge(a, b, c);
    }
    dfs(1);
    printf("%d", f[1][q]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Estia_/article/details/83621055