如何使用动态规划求矩阵中的和

How to find the sum in a matrix with dynamic programming

拜托,我想找到每行只有一个值的最大总和。我已经通过蛮力做出了决议,它是O(N ^ 5)。现在我想找到一种动态规划的方法或其他降低复杂性的方法。

例如:

矩阵:

  100   5   4   3  1

  90   80  70  60  50

  70   69  65  20  10

  60   20  10   5   1

  50   45  15   6   1

5组的解法:

  1. 100 + 90 + 70 + 60 + 50 = 370

  2. 100 + 90 + 69 + 60 + 50 = 369

  3. 100 + 90 + 70 + 60 + 45 = 365

  4. 100 + 90 + 65 + 60 + 50 = 365

  5. 100 + 90 + 69 + 60 + 45 = 364

总数:1833

暴力求和示例:

  for(int i=0; i<matrix[0].size(); i++) {
    for(int j=0; j<matrix[1].size(); j++) {
      for(int k=0; k<matrix[2].size(); k++) {
        for(int l=0; l<matrix[3].size(); l++) {
          for(int x=0; x<matrix[4].size(); x++) {
            sum.push_back(matrix[0][i] + matrix[1][j] + matrix[2][k] + matrix[3][l] + matrix[4][x]);
          }
        }
      }
    }
  }
  
sort(sum.begin(), sum.end(), mySort);

谢谢!

如果您只想要最大总和,则对每行的最大值求和。 也就是说,

M = [[100, 5, 4, 3, 1],
 [90, 80, 70, 60, 50],
 [70, 69, 65, 20, 10],
 [60, 20, 10, 5, 1],
 [50, 45, 15, 6, 1]]

sum(max(row) for row in M)

编辑

没有必要使用动态规划等。
有一个简单的规则:select 下一个数字,考虑数字与当前数字之间的差异。

这是使用 numpy 的代码。

import numpy as np
M = np.array(M)
M = -np.sort(-M, axis = 1)
k = 3

answer = []
ind = np.zeros(M.shape[0], dtype = int)
for _ in range(k):
    answer.append(sum(M[list(range(M.shape[0])), ind]))
    min_ind = np.argmin(M[list(range(len(ind))), ind] - M[list(range(len(ind))), ind+1])
    ind[min_ind] += 1

结果是 [370, 369, 365]

您可以在 O(k*log k) 时间内用 Dijkstra's algorithm 解决它。图中的节点由一个列表表示,该列表具有矩阵相应行中数字的 5 个索引。

例如在矩阵中

100 5  4  3  1
90  80 70 60 50
70  69 65 20 10
60  20 10 5  1
50  45 15 6  1

节点[0, 0, 2, 0, 1]代表数字[100, 90, 65, 60, 45]

初始节点为[0, 0, 0, 0, 0]。每个节点最多有5条出边,5个索引中的1个增加1,节点之间的距离是索引数之和的绝对差。

因此对于该矩阵,节点 [0, 0, 2, 0, 1] 的边领先:

  • [1, 0, 2, 0, 1] 距离为 100 - 5 = 95
  • [0, 1, 2, 0, 1],距离为 90 - 80 = 10
  • [0, 0, 3, 0, 1] 距离为 65 - 20 = 45
  • [0, 0, 2, 1, 1] 距离为 60 - 20 = 40
  • [0, 0, 2, 0, 2],距离为 45 - 15 = 30

通过此设置,您可以使用 Dijkstra 算法找到离初始节点最近的 k - 1 个节点。

Update 我之前用的是贪心算法,这个问题不行。这是一个更通用的解决方案。

假设我们已经找到了总和最高 m 的组合。下一个最高组合(数字 m+1)必须与其中之一相距 1 步,其中一步定义为将焦点向右移动一列中的其中一行矩阵。 (任何距离所有top m组合超过一步的组合都不可能是最高的m+1,因为你可以转换它通过撤消这些步骤之一,即返回现有组合之一,到不在顶部 m 中的更高组合。)

对于m = 1,我们知道“m最高组合”就是取第一个元素的组合矩阵的每一行(假设每一行从最高到最低排序)。那么我们可以从那里算出来:

  1. 创建一组候选组合以考虑下一个最高位置。这最初将只包含最高可能的组合(矩阵的第一列)。

  2. 确定总和最高的候选者并将其移至结果中。

  3. 找到与刚刚添加到结果中的组合相差 1 步的所有组合。将所有这些添加到候选组合集中。每轮只会添加其中的 n,其中 n 是矩阵中的行数。有些可能与之前确定的候选人重复,应忽略。

  4. 返回步骤 2。重复直到有 5 个结果。

下面是执行此操作的一些 Python 代码:

m = [
    [100, 5, 4, 3, 1],
    [90, 80, 70, 60, 50],
    [70, 69, 65, 20, 10],
    [60, 20, 10, 5, 1],
    [50, 45, 15, 6, 1]
]
n_cols = len(m[0]) # matrix width

# helper function to calculate the sum for any combination,
# where a "combination" is a list of column indexes for each row
score = lambda combo: sum(m[r][c] for r, c in enumerate(combo))

# define candidate set, initially with single highest combination
# (this set could also store the score for each combination
# to avoid calculating it repeatedly)
candidates = {tuple(0 for row in m)}
results = set()

# get 5 highest-scoring combinations
for i in range(5):
    result = max(candidates, key=score)
    results.add(result)
    candidates.remove(result)  # don't test it again
    # find combinations one step away from latest result
    # and add them to the candidates set
    for j, c in enumerate(result):
        if c+1 >= n_cols:
            continue  # don't step past edge of matrix
        combo = result[:j] + (c+1,) + result[j+1:]
        if combo not in results:
            candidates.add(combo)  # drops dups

# convert from column indexes to actual values
final = [
    [m[r][c] for r, c in enumerate(combo)]
    for combo in results
]
final.sort(key=sum, reverse=True)
print(final)
# [
#     [100, 90, 70, 60, 50]
#     [100, 90, 69, 60, 50], 
#     [100, 90, 70, 60, 45], 
#     [100, 90, 65, 60, 50], 
#     [100, 90, 69, 60, 45], 
# ]