题目
算法标签: 树形 d p dp dp, 递归, 树上背包
思路
根据题意, 树的叶子结点是用户, 中间节点是中转站, 每个用户准备一笔费用, 使得不亏本的情况下使得观看的用户数量最大, 不亏本就是总的收到用户的费用 - 线路上的花费 ≥ 0 \ge 0 ≥0, 定义状态表示 f [ i ] [ j ] f[i][j] f[i][j]表示以 i i i为根节点的子树中, 恰好选择了 j j j个叶子结点的最大利润, 也就是用户的缴费减去花费
树上背包问题都可以这样进行考虑, 给一定的体积 v v v, 能得到的最大价值是多少, 在本题当中就是分配 j j j个叶子节点的最大利润
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 3010, M = N << 1;
const int INF = 0x3f3f3f3f;
int n, m;
int head[N], ed[M], ne[M], w[M], idx;
int vals[N];
// f[i][j]表示以i为根节点的子树, 分配j个叶子结点的最大利润
int f[N][N];
void add(int u, int v, int val) {
ed[idx] = v, ne[idx] = head[u], w[idx] = val, head[u] = idx++;
}
int dfs(int u, int fa) {
if (u > n - m) {
f[u][1] = vals[u];
return 1;
}
// g代表u的子节点管辖的叶子结点数量
int sz = 0, g;
for (int i = head[u]; ~i; i = ne[i]) {
int v = ed[i];
if (v == fa) continue;
g = dfs(v, u);
sz += g;
for (int j = sz; j > 0; --j) {
for (int k = 1; k <= min(j, g); ++k) {
f[u][j] = max(f[u][j], f[u][j - k] + f[v][k] - w[i]);
}
}
}
return sz;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
memset(head, -1, sizeof head);
memset(f, -0x3f, sizeof f);
cin >> n >> m;
for (int i = 1; i <= n; ++i) f[i][0] = 0;
for (int i = 1; i <= n - m; ++i) {
int k, v, w;
cin >> k;
while (k--) {
cin >> v >> w;
add(i, v, w);
add(v, i, w);
}
}
for (int i = n - m + 1; i <= n; ++i) cin >> vals[i];
dfs(1, -1);
for (int i = m; i >= 0; --i) {
if (f[1][i] >= 0) {
cout << i << "\n";
return 0;
}
}
return 0;
}

京公网安备 11010502036488号