✨ feat(server.ts): 支持环境变量端口配置并统一命名规范
- 将硬编码端口变量从 `port` 重命名为 `PORT` 以符合常量命名约定 - 添加对 `process.env.PORT` 环境变量的支持,使部署时能动态配置端口 - 保持默认端口 7799 作为后备值,确保本地开发环境正常运行
This commit is contained in:
262
solutions/0067.MaxPathSum2/热带代数解法.md
Normal file
262
solutions/0067.MaxPathSum2/热带代数解法.md
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
对于数字三角形路径问题,这正是**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是强制起点**,必须计入总收益。热带代数通过幂等性和分配律自动传播这一约束。
|
||||||
Reference in New Issue
Block a user