P1273 有线电视网 (树上分组背包)

版权声明:若转载请附上原博客链接,谢谢! https://blog.csdn.net/Link_Ray/article/details/88936793

题解

dp[u][j]表示u的子树中选了j个终端的盈利。这里设vu的子树。
状态转移:
dp[u][j]=max{dp[v][k+t]-cost},和分组背包类似,把子树v看成一个组,然后从子树v中选t个物品。

这里要注意的是树形dp其实自动省略了一维i,本质上是dp[i][u][j],前i颗子树中第u个结点有j个物品的盈利。这里用dfs从下往上更新可以省略掉第一维,所以这里对状态更新的时候需要逆序枚举j,原理跟一维01背包一样。

https://www.luogu.org/problemnew/show/P1273

#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int INF = 0x3f3f3f3f;
const int maxn = 3000+5;
int n,m;
vector<pii> G[maxn];
int cost[maxn], dp[maxn][maxn], sum[maxn];
int tmp[maxn];
void add(int u, int v, int w) {
	G[u].push_back(make_pair(v,w));
}

void dfs(int u, int fa) {
	if(u > n-m) sum[u] = 1;
	for(int i = 0; i < G[u].size(); ++i) {
		int v = G[u][i].first;
		if(v == fa) continue;
		dfs(v,u);
		sum[u] += sum[v];

	}
	if(u > n-m)
		dp[u][1] = cost[u];
	else {

		int t = 0;
		for(int i = 0; i < G[u].size(); ++i) {
			int v = G[u][i].first;
			if(v == fa) continue;
			t += sum[v];
			//for(int j = 0; j <= t; ++j) tmp[j] = dp[u][j];
		    for(int j = t; j >= 0; --j) {
		    	for(int k = 0; k <= j; ++k) {
		    		dp[u][j] = max(dp[u][j],dp[u][j-k]+dp[v][k]-G[u][i].second);

		    	}
		    	// cout << u <<" " << j <<" " << dp[u][j] << endl;
		    }
		}
	}
}
int main() {
	memset(dp, -INF, sizeof dp);
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i) dp[i][0] = 0;
	for(int i = 1; i <= n-m; ++i) {
		int k;
		scanf("%d", &k);
		int v,w;
		for(int j = 0; j < k; ++j) {
			scanf("%d%d", &v, &w);
			add(i,v,w);
		}
	}
	for(int j = n-m+1; j <= n; ++j) 
		scanf("%d", &cost[j]);
	dfs(1,-1);
	int j;
	for(j = m; j >= 0; --j) {
		if(dp[1][j] >= 0) break;
	}
	cout << j << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Link_Ray/article/details/88936793
今日推荐