在动态规划(DP)中,“人人为我”和“我为人人”是两种不同的状态转移思想,用于描述状态之间的依赖关系和更新方向:
1. 人人为我(Pull-based DP)
- 核心思想:当前状态的值 依赖于其他已知状态的值。
即:用“周围点的值”来计算“当前点的值”。 
- 实现方式:
 在计算状态dp[i]时,需要主动获取其他状态(如dp[i-1]、dp[j])的值,通过它们推导出dp[i]。
- 典型场景:
- 线性DP:如斐波那契数列 dp[i] = dp[i-1] + dp[i-2]。
- 区间DP:如石子合并问题中,f[i][j]的值依赖于子区间f[i][k]和f[k+1][j]的值 [3]。
 
- 线性DP:如斐波那契数列 
- 特点:
- 依赖前置状态:必须先计算所有依赖的子状态,才能计算当前状态。
- 自然符合拓扑序:按依赖顺序递推(如从 i=0到i=n)。
 
示例:
# 斐波那契数列(人人为我)
dp[0] = 0; dp[1] = 1
for i in range(2, n+1):
    dp[i] = dp[i-1] + dp[i-2]  # 用 dp[i-1] 和 dp[i-2] 计算 dp[i]
2. 我为人人(Push-based DP)
- 核心思想:当前状态的值 主动更新其他状态的值。
即:用“当前点的值”去更新“周围点的值”。 
- 实现方式:
 计算完状态dp[i]后,立即用其值更新后续依赖它的状态(如dp[i+1]、dp[j])。
- 典型场景:
- 最短路问题:如 Dijkstra 算法中,用当前节点更新邻居节点的距离。
- 状态压缩DP:如旅行商问题(TSP)中,更新状态集合的代价 [6]。
 
- 特点:
- 主动传播结果:状态计算后立即影响后续状态。
- 需显式控制更新顺序:如按拓扑序或优先队列处理 [6]。
 
示例:
# 最短路径更新(我为人人)
dist[0] = 0
for u in graph.nodes:
    for v, w in graph.neighbors(u):
        # 用 dist[u] 更新 dist[v]
        if dist[u] + w < dist[v]:
            dist[v] = dist[u] + w
关键区别
| 特性 | 人人为我 | 我为人人 | 
|---|---|---|
| 方向 | 当前状态 被动接受 其他值 | 当前状态 主动推送 给其他值 | 
| 计算顺序 | 从基础状态向目标状态推进 | 从已知状态向未知状态扩展 | 
| 适用问题 | 线性递推、区间合并 [3][4] | 图算法、状态空间优化 [6] | 
滚动数组优化
- 作用:用一维数组替代二维数组,节省空间(如 dp[2][n]→dp[n])。
- 条件:
- 仅当 旧值不再被使用 时可覆盖(如 dp[i][j]只依赖dp[i-1][k])。
- 常见于 人人为我 的线性DP(如斐波那契数列)[4]。
 
- 仅当 旧值不再被使用 时可覆盖(如 
- 示例:
dp[0], dp[1] = 0, 1 for i in range(2, n+1): dp[i % 2] = dp[(i-1) % 2] + dp[(i-2) % 2] # 交替覆盖旧值
总结
- 人人为我:当前状态的值由其他状态 推导而来(如“用邻居算自己”),适合依赖关系清晰的子问题分解 [4]。
- 我为人人:当前状态的值 主动影响 其他状态(如“用自己的值更新邻居”),适合状态间存在隐式依赖的优化问题 [6]。
 滚动数组是空间优化的技巧,需确保覆盖的数据不再使用,常用于 人人为我 型DP的迭代实现 [4]。
 
     
   
        
        
        
       
        