使用更多方向的最大路径和问题的变体

Variation of max path sum problem using more directions

我不知道如何处理这个问题。

我们得到了一个 N*N 网格,其中列出了从位置 a 到位置 b 的成本。

网格中的每一行告诉我们从一个位置到另一个位置的成本(每个位置对应于成本数组中的一行)。 (如果成本数组中行 a 出现在行 b 之后,我们说位置 a 大于位置 b。每行的索引是一个位置)。我们可以选择从任何给定位置开始,并且只访问每个位置一次。在我们访问的每个位置 p,我们必须已经访问了所有小于 p 的位置,或者没有小于 p 的位置。

我们的任务是找到有效路径的最大成本总和。

如果成本数组是:

[[0, 9, 1],
 [5, 0, 2],
 [4, 6, 0]]

因此最大成本将为 13,因为最昂贵的有效路径从位置 2 -> 位置 0 -> 位置 1 开始。

第一行告诉我们从位置 0 到位置 0(保持在同一个位置,花费我们 0)、0 到位置 1(花费我们 9)和 0 到位置 2(花费我们 1)。第二行和第三行遵循相同的模式。

关于您可以访问的位置的要求意味着,在您从某个位置 i 开始后,您将被迫反复移动到较低的位置,直到您到达位置 0。此时,您必须连续上升通过所有未访问的位置。动态规划解决方案并不明显,但通过相当复杂的实现,您可以使用标准技术获得 O(n^3) DP 算法。

事实证明还有一个 O(n^2) 解决方案,这是最优的。它还使用 O(n) 额外的 space,这可能也是最优的。解决方案来自对我们访问结构的思考:有一个向下的索引序列(可能有间隙)以 0 结束,然后是一个从 0 开始的向上序列,其中包含所有其他索引。虽然有 2^n 个可能的子序列,所以我们必须考虑更多以加快速度。


两个序列

假设我们有 i 个位置,0, 1, ... i-1,并且我们将它们分成两个有序的子序列(0 除外,它位于两个序列的开头)。我们将这两个序列称为 UD,分别表示向上和向下。其中一个必须在 i-1 结束。不失一般性,假设 Ui-1 结尾,Dj >= 0.

结尾

添加位置后会发生什么i?我们要么将其添加到 U 的末尾,以便我们的序列在 ij 处结束,要么将其添加到 D 的末尾 所以我们的序列结束于 i-1i。如果我们将它添加到 UU 的路径和(我们将其定义为所有相邻索引的 cost[u][v] 的总和u,v in U) 增加 cost[i-1][i]。如果我们将位置添加到 D 的末尾,D 的路径和将增加 cost[i][j](因为它是向下序列,我们翻转了相对于 U) 的索引。

事实证明,我们只需要在子序列增长时跟踪它们的端点,以及具有这些端点的任何一对子序列的最大组合路径和。如果我们让 (i, j) 表示 Ui 结束并且 Dj 结束的状态,我们可以想想我们怎么会到这里的。

例如,在 (8,5),我们之前的状态 必须 有一个包含 7 的子序列,所以我们之前的状态必须是 (7,5).因此max-value(8,5) = max-value(7,5) + cost[7][8]。当两个端点相差不止一个时,我们总是只有一个前导状态。

现在考虑状态(8,7)。我们不可能来自 (7,7),因为唯一允许出现在两个序列中的数字是 0。所以我们可以来自 (0,7), (1,7), ... (6,7) 中的任何一个:我们可以选择使我们的路径总和最大化的那个。

def solve(costs: List[List[int]]) -> int:
    n = len(costs)
    # Deal with edge cases
    if n == 1:
        return 0
    if n == 2:
        return max(costs[0][1], costs[1][0])

    ups = [costs[0][1]]
    downs = [costs[1][0]]

    # After iteration i, ups[j] denotes the max-value of state (i, j)
    # and downs[j] denotes the max-value of state (j, i)
    for i in range(2, n):
        ups.append(max(downs[j] + costs[j][i] for j in range(i - 1)))
        downs.append(max(ups[j] + costs[i][j] for j in range(i - 1)))

        up_gain   = costs[i-1][i]
        down_gain = costs[i][i-1]

        for j in range(i - 1):
            ups[j]   += up_gain
            downs[j] += down_gain

    return max(max(ups), max(downs))