[Algorithmic Toolbox学习笔记][week6]Placing Parentheses

问题描述

假设当前给出一组算式 5 - 8 + 7 * 4 - 8 + 9,在你只能加括号的情况下,怎样去获取其最大值?

比如:

1. ( 5 - 8 ) + ( 7 x 4 ) - ( 8 + 9) = 8

2. ( 5 - 8 + 7 ) x ( 4 - 8 + 9 ) = 25

算法解析 

假设我们用下图来代表这个算式。d1表示第一个数字,dn表示最后一个数字。op表示的是运算符号(运算符号可以为 + - *),op1表示的是d1和d2之间的运算符号,以此类推。

算法思路:要获得这个算式结果的最大值,我们要用到dynamic programming的逻辑,先计算出更小的算式的结果,然后再去找到原等式结果的最大值。

比如我们要计算 ( 5 - 8 + 7 ) 的值,我们的思考逻辑如下:

1. 假设先计算 5 - 8 的值,-3 + 7 = 4 --> 即最后一步做加法运算

2. 假设先计算 8 + 7 的值,5 - 15 = -10 --> 即最后一步做减法运算

因此针对 ( 5 - 8 + 7 ) :

其结果的最小值:min ( 5 - 8 + 7 ) = -10

其结果的最大值:max ( 5 - 8 + 7 ) = 4

推论:
1. 如果我们要求算式 1 - ( 5 - 8 + 7) 的最大值。

    可以知道此时 1 - ( -10 ) 的值更大,因此我们要使用 min ( 5 - 8 + 7 )

2. 如果我们要求算式 1 + ( 5 - 8 + 7) 的最大值。

    可以知道此时 1 + 4 的值更大,因此我们要使用 max ( 5 - 8 + 7 )

结论:

1. 如果算式中有多个运算符,我们依次假设当前运算符为最后一次运算

2. 针对运算符号左侧和右侧的算式,我们分别要计算出其最小值和最大值
    --> 即针对任意的算式,我们都要分别获得其最大值和最小值

获取子算式的最大值和最小值

 5 - 8 + 7 * 4 - 8 + 9 为例,假设当前最后一次做的是乘法:

即 ( 5 - 8 + 7 ) * ( 4 - 8 + 9 )

1. 左边的最小值:min( 5 - 8 + 7 ) = ( 5 - ( 8 + 7 )) = -10

2. 左边的最大值:max( 5 - 8 + 7 ) = (( 5 - 8 ) + 7) = 4

3. 右边的最小值:min( 4 - 8 + 9 ) = ( 4 - ( 8 + 9 )) = -13

4. 右边的最大值:max( 4 - 8 + 9 ) = (( 4 - 8 ) + 9 ) = 5

在知道运算符号两边算式的最大值和最小值后,由于我们不知道怎样相乘能够得到最大值,因此我们要遍历所有情况:

1. a = max(左边) * max(右边) = 4 * 5 = 20

2. b = max(左边) * min(右边) = 4 * -13 = -52

3. c = min(左边) * min(右边) = -10 * -13 = 130 

4. d = min(左边) * max(右边) = -10 * 5 = -50

由此可见,当最后一步为乘法的时候,

算式的最大值为:max ( a, b, c, d ) = 130

算式的最小值为:min ( a, b, c, d ) = -52

公式推理:

假设 M( i , j ) 表示算式结果的最大值

假设 m( i , j ) 表示算式结果的最小值

假设我们最后一步要进行的运算方式为 opk (可能是乘法,加法,或是减法):

for k from i to j

如何获 i 和 j,即如何获取子算式?

假设我们有这样的两个表格,一个表格用于填写算式的最大值,一个表格用于填写算式的最小值。注:上图中5和8之间的运算符号是减号,8和7之间的运算符号是加号...

红色格子(0,1):表示 5 - 8 能够得到的最大值(最小值)
绿色格子(1,3):表示 8 + 7 * 4 能够得到的最大值(最小值)
紫色格子(1,4):表示 8 + 7 * 4 - 8 能够得到的最大值(最小值)
蓝色格子(2,5):表示 7 * 4 - 8 + 9 能够得到的最大值(最小值)

因此,要知道5 - 8 + 7 * 4 - 8 + 9能够得到的最大值,我们只需要知道在填写最大值的表格里面,格子(0,5)的值是多少即可。

获取格子中的值

假设我们要获取(1,4)的最大值和最小值,即 8 + 7 * 4 - 8  的最大值和最小值。

根据上方“公式推理”中的逻辑我们可以知道:

1. 当k为1的时候 --> 最后的运算符号为 + :

a = M(1,1) + M(2,4)

b = M(1,1) + m(2,4)

c = m(1,1) + m(2,4)

d = m(1,1) + M(2,4)

max(1,4) = max (a, b, c, d)

min(1,4) = max (a, b, c, d)

2. 当k为2的时候 --> 最后的运算符号为 * :

a = M(1,2) * M(3,4)

b = M(1,2) * m(3,4)

c = m(1,2) * m(3,4)

d = m(1,2) * M(3,4)

max(1,4) = max (a, b, c, d)

min(1,4) = max (a, b, c, d)

3. 当k为3的时候 --> 最后的运算符号为 - :

a = M(1,3) - M(4,4)

b = M(1,3) - m(4,4)

c = m(1,3) - m(4,4)

d = m(1,3) - M(4,4)

max(1,4) = max (a, b, c, d)

min(1,4) = max (a, b, c, d)

由此可见,要获得下图中红色格子(1,4)的值,我们要先知道如下蓝色格子中的值。

 

 同理要获得下图中红色格子(1,2)的值,我们要先知道如下蓝色格子中的值。

 

由此我们可以推断,要获得右上方格子里的值,我们要先获得靠近对角线上的格子里的值。即先获对角线上的格子里的值(黑色的线),再获取只有两个数字的算式的值(红色的线),然后在获取只有3个数字的算式的值(紫色的线),以此类推最后获取到原算式的值。

 代码逻辑如下

 

Implementation

def evalaa(a,b,op):
    if op == '+':
        return a + b
    if op == '-':
        return a - b
    if op == '*':
        return a * b

#计算坐标对应的算式的最大值和最小值
def min_and_max(i, j, M, m):
    min_value = float('inf') #初始化要计算的格子里的最小值是正无穷
    max_value = float('-inf') #初始化要计算的格子里的最大值是负无穷

    for k in range(i,j):
        a = evalaa(M[i][k], M[k+1][j], op[k])
        b = evalaa(M[i][k], m[k+1][j], op[k])
        c = evalaa(m[i][k], m[k+1][j], op[k])
        d = evalaa(m[i][k], M[k+1][j], op[k])

        min_value = min(min_value, a, b, c, d)
        max_value = max(max_value, a, b, c, d)

    return (min_value, max_value)

def parentheses(op, numbers):
    n = len(numbers)
    M = [[0 for x in range(n)] for x in range(n)] #创建存放最大值的表格
    m = [[0 for x in range(n)] for x in range(n)] #创建存放最小值的表格

    #将表格对角线上的格子的值填充为本身(1个数字的算式不涉及运算因此是本身)
    for i in range(n):
        m[i][i] = int(numbers[i])
        M[i][i] = int(numbers[i])
    #按照一定逻辑依次获取坐标位置
    for s in range(1, n):
        for i in range(0,n-s):
            j = i + s
            m[i][j], M[i][j] = min_and_max(i,j,M,m)

    return M[0][n-1]

dataset = '5-8+7*4-8+9'
op = dataset[1:len(dataset):2]
numbers = dataset[0:len(dataset)+1:2]

print(parentheses(op, numbers))

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值