Update I previously used a greedy algorithm, which doesn't work for this problem. Here is a more general solution.
Suppose we've already found the combinations with the top m highest sums. The next highest combination (number m+1) must be 1 step away from one of these, where a step is defined as shifting focus one column to the right in one of the rows of the matrix. (Any combination that is more than one step away from all of the top m combinations cannot be the m+1 highest, because you can convert it to a higher one that is not in the top m by undoing one of those steps, i.e., moving back toward one of the existing combinations.)
For m = 1, we know that the "m highest combinations" just means the combination made by taking the first element of each row of the matrix (assuming each row is sorted from highest to lowest). So then we can work out from there:
Create a set of candidate combinations to consider for the next highest position. This will initially hold only the highest possible combination (first column of the matrix).
Identify the candidate with the highest sum and move that to the results.
Find all the combinations that are 1 step away from the one that was just added to the results. Add all of these to the set of candidate combinations. Only n of these will be added each round, where n is the number of rows in the matrix. Some may be duplicates of previously identified candidates, which should be ignored.
Go back to step 2. Repeat until there are 5 results.
Here is some Python code that does this:
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],
# ]