[JZOJ 5788] 餐馆

思路:
考虑树形dp。
我们设\(dp[i][j][0/1]\)表示在\(i\)为根的子树中花费\(j\)单位时间,最终回到/不必回到\(i\)的最大收益。
转移三种:
\(dp[x][j][0] = max(dp[x][j][0],dp[x][j - k - 2][0] + dp[y][k][1]);\)
\(dp[x][j][0] = max(dp[x][j][0],dp[x][j - k - 1][1] + dp[y][k][0]);\)
\(dp[x][j][1] = max(dp[x][j][1],dp[x][j - k - 2][1] + dp[y][k][1]);\)
搞定.

#include <bits/stdc++.h>
using namespace std;
inline int read() {
	int q=0,f=1;char ch = getchar();
	while(!isdigit(ch)){
		if(ch=='-')f = -1;ch=getchar();
	}
	while(isdigit(ch)){
		q=q*10+ch-'0';ch=getchar();
	}
	return q*f;
}
const int maxn = 600;
struct edge {
	int to;
	int nxt;
}e[maxn << 1];
int cnt;
int head[maxn<<1];
inline void add_edge(int u,int v) {
	e[++cnt].to = v;
	e[cnt].nxt = head[u];
	head[u] = cnt;
}
inline void Add_edge(int u,int v) {
	add_edge(u,v);
	add_edge(v,u);
	return;
}
int dp[maxn][maxn][2];
int m;int n;
int a[maxn];
inline void dfs(int x,int fa) {
	for(int i = head[x];i;i=e[i].nxt) {
		int y = e[i].to;
		if(y == fa) continue;
		dfs(y,x);
		for(int j = m;j >= 2; --j) {
			for(int k = 0;k <= m; ++k) {
				if(j - k - 2 < 0) break;
				dp[x][j][0] = max(dp[x][j][0],dp[x][j - k - 2][0] + dp[y][k][1]);
			}
		}
		for(int j = m;j >= 1; --j) {
			for(int k = 0;k <= m; ++k) {
				if(j - k - 1 < 0) break;
				dp[x][j][0] = max(dp[x][j][0],dp[x][j - k - 1][1] + dp[y][k][0]);
			}
		}
		for(int j = m;j >= 2; --j) {
			for(int k = 0;k <= m; ++k) {
				if(j - k - 2 < 0) break;
				dp[x][j][1] = max(dp[x][j][1],dp[x][j - k - 2][1] + dp[y][k][1]);
			}
		}
	}
	for(int i = m;i >= 1; --i) {
		dp[x][i][0] = max(dp[x][i][0],dp[x][i - 1][0] + a[x]);
		dp[x][i][1] = max(dp[x][i][1],dp[x][i - 1][1] + a[x]);
	}
}
int main () {
	#ifdef ONLINE_JUDGE
		freopen("dostavljac.in","r",stdin);
		freopen("dostavljac.out","w",stdout);
	#endif
	n = read(),m = read();
	for(int i = 1;i <= n; ++i) {
		a[i] = read();
	}
	for(int i = 1;i < n; ++i) {
		int x = read(),y = read();
		Add_edge(x,y);
	}
	dfs(1,0);
	printf("%d\n",dp[1][m][0]);
	return 0;
}
原文地址:https://www.cnblogs.com/akoasm/p/9563839.html