【恐怖の算法】 树形DP
【恐怖の算法】 树型DP
引入
树形 DP,即在树上进行的 DP。由于树固有的递归性质,树形 DP 一般都是递归进行的。
基础
以下面这道题为例,介绍一下树形 DP 的一般过程。
我们设 \(f(i,0/1)\) 代表以 \(i\) 为根的子树的最优解(第二维的值为 \(0\) 代表 \(i\) 不参加舞会的情况,\(1\) 代表 \(i\) 参加舞会的情况)。
对于每个状态,都存在两种决策(其中下面的 \(x\) 都是 \(i\) 的儿子):
上司不参加舞会时,下属可以参加,也可以不参加,此时有 \(f(i,0) = \sum\max \{f(x,1),f(x,0)\}\);
上司参加舞会时,下属都不会参加,此时有 \(f(i,1) = \sum{f(x,0)} + a_i\)。
我们可以通过 \(DFS\),在返回上一层时更新当前结点的最优解。
#include <algorithm> #include <iostream> using namespace std; struct edge { int v, next; } e[6005]; int head[6005], n, cnt, f[6005][2], ans, is_h[6005], vis[6005]; void addedge(int u, int v) { // 建图 e[++cnt].v = v; e[cnt].next = head[u]; head[u] = cnt; } void calc(int k) { vis[k] = 1; for (int i = head[k]; i; i = e[i].next) { // 枚举该结点的每个子结点 if (vis[e[i].v]) continue; calc(e[i].v); f[k][1] += f[e[i].v][0]; f[k][0] += max(f[e[i].v][0], f[e[i].v][1]); // 转移方程 } return; } int main() { cin.tie(nullptr)->sync_with_stdio(false); cin >> n; for (int i = 1; i <= n; i++) cin >> f[i][1]; for (int i = 1; i < n; i++) { int l, k; cin >> l >> k; is_h[l] = 1; addedge(k, l); } for (int i = 1; i <= n; i++) if (!is_h[i]) { // 从根结点开始DFS calc(i); cout << max(f[i][1], f[i][0]); return 0; } }
通常,树形 \(DP\) 状态一般都为当前节点的最优解。先 \(DFS\) 遍历子树的所有最优解,然后向上传递给子树的父节点来转移,最终根节点的值即为所求的最优解.
噩梦算法の终结~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix