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
",ans);
}

原文地址:https://www.cnblogs.com/zhanglichen/p/14632719.html