最大总和问题
将正整数排成等边三角形(也叫数塔),三角形的底边有个数,下图给出了的一个例子。从三角形顶点出发通过一系列相邻整数(在图中用正方形表示),如何使得到达底边时的总和最大?
问题分析与解决思路
数塔由顶部到底部的路径,由顶向下考虑和由底向上考虑,算法都是一样的。这里为了明确结果,给出由底向上分析时的解题思路。
将数塔的规模缩小到只有顶部3层,可以简单地分析出,最大总和为第二层最大的总和加上第一层自身,拓展到一般情况,第二层每一个单位最大的总和为它下一层连接的数中,最大的那一个。将结论推向至整个数塔,即第i行第j项的数,它到底部的最大总和,为第i+1行第j项和第i+1行第j+1项中较大那一个,与第i行第j项的和。设tower[i][j]为原数塔第i行第j项,result[i][j]为第i行第j项为数塔顶时的最大总和问题结果,公式表达为:
result[i][j] = tower[i][j] + max(result[i+1][j], result[i+1][j+1])
得到状态转移方程,于是可利用动态规划求解该问题。
算法描述
记数塔(tower)的规模,即数塔的层数为n。
我们将上述分析过程和解决的思路进一步归纳为以下步骤:
(1)初始化结果数塔(result )最底层为原数塔最底层。
(2)根据result[i][j] = tower[i][j] + max(result[i+1][j], result[i+1][j+1]),由底向上地开始计算结果数塔,同时也可以利用另一个表用于记录路径。
(3)返回结果数塔,其中result[0][0]为该问题的结果。
复杂度分析
数塔可以用邻接矩阵描述,而使用动态规划时同样创建了一张动态规划表,所以使用到的数据结构为二维数组。
本算法初始化时有n次赋值操作,加上运算时(1+2+...+n-1)次赋值操作,一共n+(1+2+...+n-1)次赋值,所以时间复杂度为θ(n^2)。
运算时创建了一个额外的二维数组——结果数塔,所以空间复杂度为n2,所以空间复杂度为θ(n^2)。
运行结果
利用了matplotlib输出。
代码
from pprint import pprint
import networkx as nx
import matplotlib.pyplot as plt
def dataTower(tower):
heigh = len(tower)
lenth = len(tower[heigh-1])
result = [[None for x in range(lenth)] for y in range(heigh)]
path = [[None for x in range(lenth)] for y in range(heigh)]
#初始化结果数塔最底层
for i in range(lenth):
result[heigh-1][i] = tower[heigh-1][i]
#开始计算结果数塔及路径
for i in range(heigh-2, -1, -1):
for j in range(i+1):
if(result[i+1][j] > result[i+1][j+1]):
result[i][j] = tower[i][j] + result[i + 1][j]
path[i][j] = '|'
else:
result[i][j] = tower[i][j] + result[i+1][j+1]
path[i][j] = '`'
return result, path
def draw(G, note_labels, position, values=None):
plt.figure()
nx.draw_networkx_nodes(G, pos=position, node_color=values if values
else 'w' , node_size=300)
nx.draw_networkx_edges(G, pos=position)
nx.draw_networkx_labels(G, position, labels=note_labels, font_size=8)
if __name__ == '__main__':
tower = [
[9],
[12,15],
[10,6,8],
[2,18,9,5],
[19,7,10,4,16]
]
heigh = len(tower)
lenth = len(tower[heigh-1])
G = nx.DiGraph()
for i in range(heigh-1):
for j in range(i+1):
G.add_edge((i,j), (i+1,j))
G.add_edge((i,j), (i+1,j+1))
note_labels = dict()
for p in G.nodes:
note_labels[p] = tower[p[0]][p[1]]
position = dict()
position[(0,0)] = (0, 0)
for i in range(heigh-1):
for j in range(i+1):
pos = position[(i,j)]
position[(i+1,j)] = (pos[0]-0.5, pos[1]-1)
position[(i+1,j+1)] = (pos[0]+0.5, pos[1]-1)
#输出原图形
draw(G, note_labels, position)
#动态规划求结果
result, path = dataTower(tower)
pprint(result)
pprint(path)
note_labels = dict()
for p in G.nodes:
note_labels[p] = result[p[0]][p[1]]
colors = dict()
colors[(0,0)] = 'c'
j = 0
for i in range(heigh-1):
if path[i][j] == '|':
colors[(i+1,j)] = 'c'
elif path[i][j] == '`':
j += 1
colors[(i+1,j)] = 'c'
else:
colors[(i+1,j)] = 'w'
values = [colors.get(node, 'w') for node in G.nodes()]
#输出结果路径
draw(G, note_labels, position, values)
plt.show()