Files
SolutionEuler/solutions/0067.MaxPathSum2/热带代数解法.md
Sidney Zhang 29b522606a feat(server.ts): 支持环境变量端口配置并统一命名规范
- 将硬编码端口变量从 `port` 重命名为 `PORT` 以符合常量命名约定
- 添加对 `process.env.PORT` 环境变量的支持,使部署时能动态配置端口
- 保持默认端口 7799 作为后备值,确保本地开发环境正常运行
2025-12-17 18:30:28 +08:00

263 lines
7.9 KiB
Markdown
Raw 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是强制起点**,必须计入总收益。热带代数通过幂等性和分配律自动传播这一约束。