P1273 有线电视网 树形DP 树上背包

P1273 有线电视网 树形DP 树上背包

题意

给定一颗有根树,要求选择最多的叶子结点,并且使得达到这些叶子结点的花费(可以理解成到叶子的路径权值和) 小于等于选择的叶子结点的点权和。

[2leq nleq 3000\ 1leq m leq n - 1 ]

分析

经典的树上背包问题。

可以对一些叶子结点进行选或者不选的决策

以下参考洛谷 yqw2486的题解

状态转移方程

[dp[i][u][j] = max(dp[i - 1][u][j - k] + dp[v的总儿子数][v][k] - w(u,v)) ]

(dp[i][u][j])表示以(u)为根的子树的前(i)棵子树中选择(j)个节点能够获得的最大费用

边界

[dp[u][1] = w[u]\ dp[u][0] = 0 ]

显然我们可以类似01背包的办法滚动一维

[dp[u][j] = max(dp[u][j-k] + dp[v][k] - w(u,v)) ]

代码

struct Edge {
    int v;
    int w;
    Edge(int _v, int _w) {
        v = _v;
        w = _w;
    }
};

vector<Edge> e[3005];
int w[3005];
int dp[3005][3005];
int n, m;

int dfs(int u) {
    if (u > n - m) {
        dp[u][1] = w[u];
        dp[u][0] = 0;
        return 1;
    }
    dp[u][0] = 0;
    int siz = 0;
    for (auto v : e[u]) {
        int val = v.w;
        int vv = v.v;
        int t = dfs(vv);
        siz += t;
        for(int j = siz;j > 0;j--)
            for (int k = 0; k <= min(j, t); k++) 
                dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[vv][k] - val);
            
    }
    return siz;
}

int main() {
    n = readint();
    m = readint();
    for (int i = 1; i <= n - m; i++) {
        int k = readint();
        for (int j = 1; j <= k; j++) {
            int x = readint();
            int y = readint();
            e[i].push_back(Edge(x, y));
        }
    }
    for (int i = n - m + 1; i <= n; i++)
        w[i] = readint();
    for (int i = 0; i < 3005; i++)
        for (int j = 0; j < 3005; j++)
            dp[i][j] = -1e9;
    for (int i = 0; i < 3005; i++)
        dp[0][i] = 0;
    dfs(1);
    for (int i = m; i >= 0; i--)
        if (dp[1][i] >= 0) {
            printf("%d", i);
            break;
        }
}
原文地址:https://www.cnblogs.com/hznumqf/p/13871407.html