带权DAG中排除直接相连顶点的最大顶点和计算方法问询
带权DAG中排除相邻顶点的最大权重和——高效DP解法(类似Viterbi思路)
完全可以借助DAG的拓扑排序特性,结合**动态规划(状态转移逻辑和Viterbi算法异曲同工)**解决这个问题,时间复杂度直接降到O(V+E),比你提到的暴力枚举解法高效太多。
这个问题本质是求DAG的最大权重独立集:选中的顶点集合里任意两个都不能直接相连,同时总权重最大。下面是具体实现思路:
核心逻辑:逆拓扑排序 + 状态转移DP
Viterbi算法的核心是通过记录每个状态的最优值,一步步推导全局最优。这里我们照搬这个思路,给每个顶点定义两种状态,配合DAG的逆拓扑顺序(从叶子往根处理)来计算:
1. 先做逆拓扑排序
把DAG转换成逆拓扑序列——从没有出边的叶子节点开始处理,直到根节点。这样能保证处理某个顶点时,它的所有后继子节点都已经计算完毕。
2. 定义DP状态
对每个顶点v,我们记录两个状态值:
dp[v][1]:选中v时,以v为根的子图能得到的最大独立集权重和。dp[v][0]:不选中v时,以v为根的子图能得到的最大独立集权重和。
3. 状态转移规则
按照逆拓扑顺序逐个处理顶点:
- 如果选中
v,那所有和v直接相连的后继节点都不能选,所以:dp[v][1] = v的权重 + 所有v的后继节点的dp[u][0]之和 - 如果不选中
v,那每个后继节点都可以选或不选(取各自的最大值),所以:dp[v][0] = 所有v的后继节点的max(dp[u][0], dp[u][1])之和
4. 得到最终结果
处理完所有顶点后,根节点的max(dp[根][0], dp[根][1])就是整个DAG的最大权重和。如果DAG有多个独立子图,就分别计算每个子图的最大值再相加。
对应示例验证
拿你说的第一个树状DAG例子:
- 叶子节点H、I、E、F没有后继,所以
dp[H][1] = H的权重,dp[H][0] = 0,其他叶子同理。 - 处理B(E、F的父节点):
dp[B][1] = B的权重 + 0 + 0;dp[B][0] = max(0, E的权重) + max(0, F的权重) = E+F的权重。 - 最后比较选A和不选A的情况:
max(A的权重 + dp[B][0] + dp[C][0], max(dp[B][0], dp[B][1]) + max(dp[C][0], dp[C][1])),结果是不选A的总和更大(30),和示例一致。
第二个例子中,选A、B、E、F的总和更高,说明dp[A][1] = A的权重 + dp[B][0] + dp[C][0]的结果超过了不选A的情况,最终取这个值(40)。
复杂度对比
- 时间复杂度:O(V+E),每个顶点和每条边只处理一次,比暴力的O(2^V E)快了几个数量级。
- 空间复杂度:O(V),只需要存储每个顶点的两个状态值。
这种方法和Viterbi算法的核心逻辑一致:通过状态记录(选/不选)和明确的转移规则,每一步都保留当前最优解,彻底避免了暴力枚举的指数级开销。
内容的提问来源于stack exchange,提问作者Asher Ross
相关产品推荐
相关产品推荐

