问题描述
假设当前给出一组算式 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))