P1273 有线电视网
P1273 有线电视网
大意
边具有边权,叶子节点具有权值,求最大的选择叶子节点的数量,使得叶子结点的权值之和大于等于需要选的边权和。
思路
首先,这个题目的建图太抽象了,需要好好理解一下。
然后我们考虑树上背包。
我们定义 \(f_{u, j}\) 表示,在 \(u\) 这颗子树内选择 \(j\) 个叶子节点的最大收益(叶子点权和 \(-\) 需要的边权和)。
那么我们显然有转移:
\[f_{u, j} = \max{f_{u, j}, f_{u, j - k} + f_{v, k} + w(u, v)}
\]
显然如果你选择了 \(v\) 这个子树所产生的收益,那么你就必须承担 \(u - v\) 这条边的代价。
然后随便作一些上下界优化即可。
代码
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int MAXN = 3005;
int n, m;
struct node{
int u, w;
};
vector<node> e[MAXN];
int a[MAXN], sz[MAXN];
int f[MAXN][MAXN];
void dfs(int u){
f[u][0] = 0;
if(e[u].empty()){
f[u][1] = a[u];
return;
}
for(int i = 0;i < e[u].size();i ++){
int v = e[u][i].u, w = e[u][i].w;
dfs(v);
for(int j = min(m, sz[u]);j >= 1;j --){
for(int k = max(0, j - sz[u] - sz[v]);k <= min(j, sz[v]);k ++){
f[u][j] = max(f[u][j], f[u][j - k] + f[v][k] - w);
}
}
}
}
void dfs2(int u){
sz[u] = 1;
for(int i = 0;i < e[u].size();i ++){
int v = e[u][i].u;
dfs2(v);
sz[u] += sz[v];
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
memset(f, -0x3f3f3f3f, sizeof(f));
cin >> n >> m;
for(int i = 1;i <= n - m;i ++){
int k; cin >> k;
for(int j = 1;j <= k;j ++){
int c, w; cin >> c >> w;
e[i].push_back({c, w});
}
}
for(int i = n - m + 1;i <= n;i ++){
cin >> a[i];
}
dfs2(1);
dfs(1);
int ans = 0;
for(int i = m;i >= 0;i --){
if(f[1][i] >= 0){
ans = i;
break;
}
}
cout << ans << '\n';
return 0;
}
本文来自一名高中生,作者:To_Carpe_Diem

浙公网安备 33010602011771号