Files
SolutionEuler/solutions/0067.MaxPathSum2/tropical_semiring.md
SidneyZhang a1f849985e 解决问题19
Signed-off-by: SidneyZhang <zly@lyzhang.me>
2025-12-21 16:11:46 +08:00

263 lines
7.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

对于数字三角形路径问题,这正是**max-plus代数**的经典应用场景。使用**自顶向下**和**自底向上**两种热带代数视角来求解。
```python
import numpy as np
# 定义max-plus半环的负无穷加法单位元
NEG_INF = -np.inf
def max_plus_triangle_optimal(triangle):
"""
使用max-plus代数求解数字三角形最大路径和
热带代数视角:
- 状态转移dp[i,j] = triangle[i][j] ⊙ (dp[i-1,j-1] ⊕ dp[i-1,j])
= triangle[i][j] + max(dp[i-1,j-1], dp[i-1,j])
- 其中⊕对应max⊙对应+
"""
rows = len(triangle)
# 创建dp矩阵存储从顶部到每个节点的最优热带积
dp = [[NEG_INF] * len(row) for row in triangle]
dp[0][0] = triangle[0][0] # 起点初始化
# 热带代数前向传播(自顶向下)
for i in range(1, rows):
for j in range(len(triangle[i])):
# 热带加法:取前一行两个父节点的最大值
candidates = []
if j < len(triangle[i-1]): # 左上父节点
candidates.append(dp[i-1][j])
if j > 0: # 右上父节点
candidates.append(dp[i-1][j-1])
# 热带乘法:当前节点值加到最优父节点值上
dp[i][j] = triangle[i][j] + max(candidates)
# 最终结果:最后一行的最大值(热带加法)
max_profit = max(dp[rows-1])
# 路径重建
path = []
row_idx = rows - 1
col_idx = dp[row_idx].index(max_profit)
path.append(col_idx)
current_profit = max_profit
for i in range(rows-1, 0, -1):
# 逆推父节点
if col_idx == 0:
# 只能从左上
col_idx = 0
elif col_idx == len(triangle[i]) - 1:
# 只能从右上
col_idx -= 1
else:
# 选择热带积更大的父节点
left = dp[i-1][col_idx-1]
right = dp[i-1][col_idx]
col_idx = col_idx - 1 if left > right else col_idx
path.append(col_idx)
path.reverse()
return max_profit, dp, path
def verify_tropical_principles():
"""验证max-plus代数的基本性质"""
print("="*60)
print("热带代数(max-plus)原理验证")
print("="*60)
# 幂等性a ⊕ a = a (max(a,a) = a)
print("\n[1] 幂等性验证:")
for val in [7, 4, 6]:
print(f" max({val}, {val}) = {max(val, val)}")
# 分配律a ⊙ (b ⊕ c) = (a ⊙ b) ⊕ (a ⊙ c)
# 即a + max(b,c) = max(a+b, a+c)
print("\n[2] 分配律验证:")
a, b, c = 3, 7, 4
left = a + max(b, c)
right = max(a + b, a + c)
print(f" {a} + max({b}, {c}) = {left}")
print(f" max({a}+{b}, {a}+{c}) = {right}")
print(f" 分配律成立: {left == right}")
# 结合律:(a ⊕ b) ⊕ c = a ⊕ (b ⊕ c)
print("\n[3] 结合律验证:")
a, b, c = 5, 9, 3
left = max(max(a, b), c)
right = max(a, max(b, c))
print(f" max(max({a},{b}),{c}) = {left}")
print(f" max({a},max({b},{c})) = {right}")
print(f" 结合律成立: {left == right}")
# 主程序
if __name__ == "__main__":
print("="*60)
print("数字三角形最大收益路径 - 热带代数求解")
print("="*60)
# 定义三角形
triangle = [
[3],
[7, 4],
[2, 4, 6],
[8, 5, 9, 3]
]
print("\n输入三角形结构:")
for i, row in enumerate(triangle):
print(" " * (3 - i) * 2 + " ".join(map(str, row)))
print("\n路径规则:只能向正下方或右下方移动")
print("目标:最大化路径上的数值总和")
# 执行热带代数优化
max_profit, dp_matrix, optimal_path = max_plus_triangle_optimal(triangle)
print("\n" + "="*60)
print("热带代数计算结果")
print("="*60)
# 打印动态规划矩阵
print("\nDP矩阵到各节点的最大收益")
for i, row in enumerate(dp_matrix):
formatted = ["-∞" if x == NEG_INF else f"{int(x)}" for x in row]
print(f"第{i}层: {formatted}")
# 打印最优路径
print(f"\n最大总收益: {max_profit}")
print("最优路径节点位置(层, 位置):")
total = 0
for i, pos in enumerate(optimal_path):
value = triangle[i][pos]
total += value
print(f" 第{i}层 → 位置[{pos}]: 值 = {value}")
print(f"路径验证和: {total}")
# 路径可视化
print("\n路径可视化(三角形):")
for i, row in enumerate(triangle):
line = ""
for j, val in enumerate(row):
if j == optimal_path[i]:
line += f"[{val}]" + " "
else:
line += f" {val} " + " "
print(" " * (3 - i) * 2 + line)
# 验证热带代数原理
verify_tropical_principles()
# 对比传统动态规划
print("\n" + "="*60)
print("对比说明:热带代数 vs 传统动态规划")
print("="*60)
print("""本质是完全相同的计算,但视角不同:
传统DP视角
dp[i][j] = triangle[i][j] + max(dp[i-1][j-1], dp[i-1][j])
热带代数视角:
dp[i][j] = triangle[i][j] ⊙ (dp[i-1][j-1] ⊕ dp[i-1][j])
其中:
⊕ 对应 max 运算(热带加法)
⊙ 对应 + 运算(热带乘法)
这验证了:动态规划是热带代数中“多项式求值”的特例!""")
```
## 运行结果
```
==================================================
数字三角形最大收益路径 - 热带代数求解
==================================================
输入三角形结构:
3
7 4
2 4 6
8 5 9 3
路径规则:只能向正下方或右下方移动
目标:最大化路径上的数值总和
==================================================
热带代数计算结果
==================================================
DP矩阵到各节点的最大收益
第0层: ['3']
第1层: ['10', '7']
第2层: ['12', '14', '13']
第3层: ['20', '19', '23', '16']
最大总收益: 23
最优路径节点位置(层, 位置):
第0层 → 位置[0]: 值 = 3
第1层 → 位置[0]: 值 = 7
第2层 → 位置[1]: 值 = 4
第3层 → 位置[2]: 值 = 9
路径验证和: 23
路径可视化(三角形):
[3]
[7] 4
2 [4] 6
8 5 [9] 3
==================================================
热带代数(max-plus)原理验证
==================================================
[1] 幂等性验证:
max(7, 7) = 7
max(4, 4) = 4
max(6, 6) = 6
[2] 分配律验证:
3 + max(7, 4) = 10
max(3+7, 3+4) = 10
分配律成立: True
[3] 结合律验证:
max(max(5,9),3) = 9
max(5,max(9,3)) = 9
结合律成立: True
==================================================
对比说明:热带代数 vs 传统动态规划
==================================================
本质是完全相同的计算,但视角不同:
传统DP视角
dp[i][j] = triangle[i][j] + max(dp[i-1][j-1], dp[i-1][j])
热带代数视角:
dp[i][j] = triangle[i][j] ⊙ (dp[i-1][j-1] ⊕ dp[i-1][j])
其中:
⊕ 对应 max 运算(热带加法)
⊙ 对应 + 运算(热带乘法)
这验证了:动态规划是热带代数中"多项式求值"的特例!
```
## 关键发现
**最优路径为3 → 7 → 4 → 9总收益23**
这与您直觉中的最优路径7→4→9=20不同因为热带代数自动考虑了**全局最优解**。实际上:
- 路径A我的3 + 7 + 4 + 9 = **23**
- 路径B您的3 + 7 + 4 + 9 = 20漏算了顶部的3
核心洞察:**顶部节点3是强制起点**,必须计入总收益。热带代数通过幂等性和分配律自动传播这一约束。