BZOJ1017 [JSOI2008]魔兽地图DotR 【树形dp + 背包dp】

题目链接

BZOJ1017

题解

orz hzwer

树形dp神题

\(f[i][j][k]\)表示\(i\)号物品恰好花费\(k\)金币,并将\(j\)个物品贡献给父亲的合成时的最大收益

计算\(f[i][j][k]\)时,我们先枚举合成了x个\(i\)号物品,计算出此时的花费各种金币下最大收益
然后就可以枚举\(j \le x\)\(k\),更新\(f[i][j][k]\)
计算最大收益,就把第\(l\)个子树的\(f[s][w * x][v]\)看做第\(l\)个物品的第\(v\)种物品【\(w * x\)是该子树需要提供的的贡献】
做分组背包即可计算出花费各种金币时可得到的最大收益

由于整体的关系不确定,我们将每棵树根拿出来再做一次分组背包即可

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = H[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define cls(s) memset(s,0,sizeof(s))
using namespace std;
const int maxn = 55,maxm = 2005,INF = 1000000000;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
int H[maxn],ne = 1;
struct EDGE{int to,nxt,w;}ed[maxm];
inline void build(int u,int v,int w){
	ed[ne] = (EDGE){v,H[u],w}; H[u] = ne++;
}
int n,m,rt,f[maxn][101][maxm],g[maxn][maxm],h[maxn][maxm];
int W[maxn],P[maxn],M[maxn],typ[maxn],fa[maxn];
void dfs(int u){
	if (!typ[u]){
		M[u] = min(M[u],m / P[u]);
		for (int i = 0; i <= M[u]; i++)
			for (int j = 0; j <= i; j++)
				f[u][j][i * P[u]] = (i - j) * W[u];
		return;
	}
	M[u] = INF;
	Redge(u){
		dfs(to = ed[k].to);
		M[u] = min(M[u],M[to] / ed[k].w);
		P[u] += P[to] * ed[k].w;
	}
	M[u] = min(M[u],m / P[u]);
	memset(g,-0x3f3f3f3f,sizeof(g));
	g[0][0] = 0;
	for (int x = M[u]; x >= 0; x--){
		int tot = 0;
		Redge(u){
			++tot; to = ed[k].to;
			for (int j = 0; j <= m; j++)
				for (int v = 0; v <= j; v++)
					g[tot][j] = max(g[tot][j],g[tot - 1][j - v] + f[to][ed[k].w * x][v]);
		}
		for (int j = 0; j <= x; j++)
			for (int v = 0; v <= m; v++){
				if (v) g[tot][v] = max(g[tot][v],g[tot][v - 1]);
				f[u][j][v] = max(f[u][j][v],g[tot][v] + W[u] * (x - j));
			}
	}
}
int main(){
	memset(f,-0x3f3f3f3f,sizeof(f));
	n = read(); m = read(); char c;
	REP(i,n){
		W[i] = read();
		c = getchar(); while (c != 'A' && c != 'B') c = getchar();
		if (c == 'A'){
			typ[i] = 1;
			int x = read(),to,w;
			while (x--){
				to = read(); w = read();
				build(i,to,w);
				fa[to] = i;
			}
		}
		else P[i] = read(),M[i] = read();
	}
	int tot = 0;
	for (int u = 1; u <= n; u++)
		if (!fa[u]){
			dfs(u); ++tot;
			for (int i = 0; i <= m; i++)
				for (int j = 0; j <= i; j++)
					h[tot][i] = max(h[tot][i],h[tot - 1][i - j] + f[u][0][j]);
		}
	int ans = 0;
	for (int i = 0; i <= m; i++) ans = max(ans,h[tot][i]);
	printf("%d\n",ans);
	return 0;
}

posted @ 2018-05-09 14:40  Mychael  阅读(164)  评论(0编辑  收藏  举报