# 0x54 动态规划-树形DP

A.没有上司的舞会 基础树形DP

emmm,蒟蒻发现自己的DP太辣鸡了。。。所以来练练DP,这题的话实际上应该算是树DP的入门题吧,转移还是挺好想的。

每次在每个节点都会有个选择,就是选还是不选,如果选的话,那么它的儿子节点就不能选,如果不选的话它的儿子节点就可以选,也就是说我们需要另开一维状态来记录每个节点是否选自己的情况,那么就很容易得出如下方程:

dp[x][0]+=max(0,max(dp[v][1],dp[v][0]));//如果不选当前节点,那么儿子节点可以任意选
dp[x][1]+=max(0,dp[v][0]);//如果选择当前节点,那么只能选择儿子节点不存在的情况

AC代码

#include<bits/stdc++.h>
using namespace std;
vector<int>son[10010];
int f[10010][2], v[10010], happy[10010], n;
void dfs(int x) {
	f[x][0] = 0, f[x][1] = happy[x];
	for (int i = 0; i < son[x].size(); ++i) {
		int y = son[x][i];
		dfs(y);
		f[x][0] += max(f[y][0], f[y][1]);
		f[x][1] += f[y][0];
	}
}
int main() {
	//freopen("in.txt", "r", stdin);
	cin >> n;
	for (int i = 1; i <= n; ++i)cin >> happy[i];//快乐指数
	for (int i = 1; i < n; ++i) {
		int x, y; cin >> x >> y;
		v[x] = 1; // x has a father
		son[y].push_back(x);
	}
	int root;
	for(int i = 1; i<= n;++i)
		if (!v[i]) {
			root = i; break;
		}
	dfs(root);
	cout << max(f[root][0], f[root][1]) << endl;
}

算法竞赛进阶指南原文:

正如深度优先和广度优先都可以对树或图进行遍历一样,除了自顶向下的递归外。我们也可以使用自底向上的Topo排序来执行树形DP。但通常前者就足够了。

B.选课 背包类树形DP

思路:

每堂课和学它必修的课连一条边。为了方便,每个入度为0的课(即可以直接选的课)与一个虚拟的n+1节点连一条边,然后在树上跑01背包即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> son[310];
int f[310][310], s[310], n, m;

void dp(int x) {
	f[x][0] = 0;
	for (int i = 0; i < son[x].size(); i++) { // 循环子节点(物品)
		int y = son[x][i];
		dp(y);
		for (int t = m; t >= 0; t--) // 倒序循环当前选课总门数(当前背包体积)
			for (int j = 0; j <= t; j++) // 循环更深子树上的选课门数(组内物品)
                f[x][t] = max(f[x][t], f[x][t-j] + f[y][j]);
			/* 或者
			for (int j = t; j >= 0; j--)
				if (t + j <= m)
					f[x][t+j] = max(f[x][t+j], f[x][t] + f[y][j]);
			这两种写法j分别用了正序和倒序循环
			是为了正确处理组内体积为0的物品(本题正序倒序都可以AC是因为体积为0的物品价值恰好也为0)
			请读者结合0/1背包问题中DP的“阶段”理论思考 */
	}
	if (x != 0) // x不为0时,选修x本身需要占用1门课,并获得相应学分
		for(int t = m; t > 0; t--)
			f[x][t] = f[x][t-1] + s[x];
}

int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
	{
		int x;
		cin >> x >> s[i];
		son[x].push_back(i);
		
	}
	memset(f, 0xcf, sizeof(f)); // -INF
	dp(0);
	cout << f[0][m] << endl;
}

C.Accumulation Degree

原文地址:https://www.cnblogs.com/RioTian/p/13518385.html