我正在尝试解决https://www.spoj.com/problems/LISA/
在问题中,给出仅具有*和+的表达式。放置支架最大值需要输出。
喜欢
1+2*3+4*5 = (1+2)*(3+4)*5 = 105
2*0*3+7+1*0*3 = 2*((0*3+7+1*0)*3) = 42
我遇到的2D动态问题解决方法的实现如下。取每个数字是矩阵的行和列,并采用从下到上的方法。
f(ci,cj) = Max( f(i,j-1) operator c(i,j) , f( i+1,j-1) operator c(i,i) )
Like 3,5 = Max of [ (3,4) * (5,5) or (3,3)+(4,5) ]
= Max of [ 7*5 or 3+20 ]
= Max of [ 35,23 ] = 35
我无法证明我得到的结果是最大且正确的。如何证明以下解决方案是最大和最优的?
-----------------------------------
C1 C2 C3 C4 C5
C1 1 3 9 13 105
C2 2 6 14 70
C3 3 7 35
C4 4 20
C5 5
答案 0 :(得分:1)
此问题可以归类为带有记忆的分而治之问题。
假设您的字符串是s = s1 op1 s2 op2 s3 op3 s4
现在您可以在每个s
处将op
分区
假设您将s
的{{1}}分区为两个字符串:
左字符串:op1
和
右字符串:s1
。
让我们说,左字符串可获取的最小值,最大值为s2 op2 s3 op3 s4
,右字符串为min1, max1
。
您可能会认为min2, max2
是最小值,而min1 op1 min2
是最大值
但尚未完成。
您需要对每个max1 op1 max2
执行此操作,并累积op
和min
的值。为什么?因为max
上的分区可能不是最佳的。
然后从所有这些分区中选择op1
和min(mins)
您可以通过记住Java中的结果来递归地实现这一点,例如:
max(maxs)
答案 1 :(得分:1)
这是我的实现:(假定查询格式正确)
class LISASolver:
def solve(self, query):
"""
takes a query, a string with numbers and +, *,
returns a tuple of min, max
"""
numbers, operators = self.parse_inputs(query)
n = len(numbers)
self.numbers = numbers
self.operators = operators
self.memo = {}
out = self.dp(0, len(numbers) - 1)
return out
def dp(self, i, j):
if i == j:
n = self.numbers[i]
return n, n
if (i, j) in self.memo:
return self.memo[(i, j)]
curmins = []
curmaxs = []
for k in range(i, j):
o = lambda a, b: (a * b) if self.operators[k] == '*' else (a + b)
leftmin, leftmax = self.dp(i, k)
rightmin, rightmax = self.dp(k + 1, j)
curmins.append(o(leftmin, rightmin))
curmaxs.append(o(leftmax, rightmax))
self.memo[(i, j)] = (min(curmins), max(curmaxs))
return self.memo[(i, j)]
def parse_inputs(self, query):
numbers = []
operators = []
current_number = []
for c in query:
if c.isdigit():
current_number.append(c)
else:
numbers.append(
int(''.join(current_number))
)
current_number = []
operators.append(c)
numbers.append(
int(''.join(current_number))
)
return numbers, operators
s = LISASolver()
query = '1+2*3+4*5'
print(s.solve(query))
>>> 27, 105
query = '2*0*3+7+1*0*3'
print(s.solve(query))
>>> 0, 42
子问题是从第i个数字到第j个数字的最优最小和最小结果。通过在每个子阵列上计算结果的最小值和最大值,然后应用递归关系,可以确保最佳性。由于存在O(n ^ 2)个子问题,每个子问题都占用O(n),因此时间复杂度为O(n ^ 3)。
编辑:
说明:
对于动态编程,至关重要的是识别什么是子问题以及子问题与其他子问题的关系。对于说n
个数字(因此称为n - 1
运算符)的问题,子问题是:通过将i
之间的数字和运算符组合在一起,可以得到的最小/最大值是多少?第j
号(含)。
基本情况为i = j
,我们只有一个数字,最小值/最大值本身。
对于任何j > i
,此问题的子问题是k的范围是i
到j - 1
左部分的最小值和最大值(i
的子问题和k
作为两个端点)和右侧部分(子问题中,k + 1
和j
作为两个端点)。对于每个k
,我们实际上要做的是将第k
个运算符用作最后一个运算符,因此,每个k
所能获得的最小值是left({{1 }})最小的权利(类似的是最大的权利)。 (请注意,运算符是operator
或*
,它们是单调的,因此我们将最小值与最小值相结合,将最大值与最大值相结合。对于更常见的问题,例如+
或-
,我们可能需要考虑很多情况,但基本结构应该相同)。因此,递归关系很简单
/
minimum(i, j) = min(minimum(i, k) [operator] minimum(k + 1, j) for each k)
我们必须一次解决每个子问题(总共(same for max)
个)并将其缓存(或像人们所说的那样记住),每个子问题都需要一个O(n^2)
循环才能解决。因此,时间复杂度为O(n)
。
对动态编程的更深入的了解确实可以帮助您解决所有类似的问题。如果您不确定在这种情况下会发生什么,我建议您阅读一些相关内容。