P273.有线电视网(树上背包)
题意:
给出一棵树。
每个叶子有点权,每条边有边权。
问你最多选多少叶子,使得总边权小于等于总点权。
题解:
树上背包。
定义状态\(f(i,j,k)\)表示子树\(i\)内选取\(j\)个叶子仅考虑\(i\)的前\(k\)个儿子的最优答案。
这里注意状态的转移的一些细节。
//f(i,j,k)表示在子树i内选择j个用户的最大收益(仅考虑前k个儿子)
#include<bits/stdc++.h>
using namespace std;
const int maxn=3005;
int f[maxn][maxn][2];
int sz[maxn];
vector<pair<int,int> > g[maxn];
int a[maxn];
int e[maxn];
int n,m;
void dfs (int x,int pre,int lst) {
int k=1;
sz[x]=1;
if (g[x].size())
f[x][0][0]=-lst+a[x];
else
f[x][1][0]=-lst+a[x];
for (pair<int,int> it:g[x]) {
int y=it.first;
int w=it.second;
if (y==pre) continue;
dfs(y,x,w);
for (int i=0;i<=sz[x];i++) {
for (int j=0;j<=sz[y];j++) {
f[x][i+j][k]=max(f[x][i+j][k],f[x][i][k^1]+f[y][j][e[y]]);
}
}
for (int i=0;i<=sz[x];i++) f[x][i][k]=max(f[x][i][k],f[x][i][k^1]);
sz[x]+=sz[y];
k^=1;
}
e[x]=(k^1);
}
int main () {
scanf("%d%d",&n,&m);
for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) for (int k=0;k<2;k++) f[i][j][k]=-1e9;
for (int i=1;i<=n-m;i++) {
int k;
scanf("%d",&k);
for (int j=1;j<=k;j++) {
int x,y;
scanf("%d%d",&x,&y);
g[i].push_back(make_pair(x,y));
}
}
for (int i=n-m+1;i<=n;i++) scanf("%d",a+i);
dfs(1,0,0);
int ans=0;
for (int j=0;j<=m;j++) if (f[1][j][e[1]]>=0) ans=j;
printf("%lld\n",ans);
}