CSP-S 提高组 C++ 树形 DP 详解及编程实例
一、树形 DP 核心思想
树形 DP 是动态规划在树结构上的应用,通过后序遍历(自底向上)的方式处理子树信息,利用子节点状态推导父节点状态。常见问题类型:
- 路径问题(最长路径、直径)
- 子树选择问题(最大独立集、最小覆盖集)
- 资源分配问题(带权值的选择)
二、基本实现步骤
- 建树:通常用邻接表存储树结构
- 确定状态:定义
dp[u][state]表示以 u 为根的子树在特定状态下的最优解 - 状态转移:根据子节点状态推导父节点状态
- 递归处理:DFS 遍历树,回溯时更新状态
三、经典例题:没有上司的舞会(洛谷 P1352)
题目描述:选择若干员工参加舞会,不能同时选择直属上下级,求最大快乐值总和。
状态定义:
dp[u][0]:不选择节点 u 为根的子树中,u 不参加舞会的最大快乐值dp[u][1]:选择节点 u 为根的子树中,u 参加舞会的最大快乐值
状态转移方程: 对于节点 u 的每个子节点 v:
- 若 u 不参加:
dp[u][0] += max(dp[v][0], dp[v][1]) - 若 u 参加:
dp[u][1] += dp[v][0](因为 u 参加了,v 就不能参加)
C++ 代码实现:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 6005;
vector<int> adj[MAXN];
int happy[MAXN];
int dp[MAXN][2]; // dp[u][0]: u not selected, dp[u][1]: u selected
bool visited[MAXN];
void dfs(int u) {
visited[u] = true;
dp[u][0] = ;
dp[u][] = happy[u];
( v : adj[u]) {
(!visited[v]) {
(v);
dp[u][] += (dp[v][], dp[v][]);
dp[u][] += dp[v][];
}
}
}
{
n;
cin >> n;
( i = ; i <= n; ++i) cin >> happy[i];
( i = ; i < n; ++i) {
l, k;
cin >> l >> k;
adj[k].(l);
}
();
cout << (dp[][], dp[][]) << endl;
;
}


