最大总和问题

 最大总和问题

将正整数排成等边三角形(也叫数塔),三角形的底边有个数,下图给出了的一个例子。从三角形顶点出发通过一系列相邻整数(在图中用正方形表示),如何使得到达底边时的总和最大?

问题分析与解决思路

数塔由顶部到底部的路径,由顶向下考虑和由底向上考虑,算法都是一样的。这里为了明确结果,给出由底向上分析时的解题思路。

将数塔的规模缩小到只有顶部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()

 

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值