How do I write a python function to maximize sum of N X N upper left sub-matrix from given 2N X 2N matrix?

Question:

I have this problem to solve: Given a 2N x 2N matrix of integers, you are allowed to reverse any row or column any number of times and in any order. The task is to calculate the maximum sum of the upper-left N X N submatrix i.e. the sum of elements of the submatrix from (0, 0) to (N – 1, N – 1).
For example, given a 4 X 4 matrix:

    [112, 42, 83, 119,
      56, 125, 56, 49,
      15, 78, 101, 43,
      62, 98, 114, 108]

The function can reverse column 2 and row 0 to get:

    [119, 114, 42, 112,
      56, 125, 101, 49,
      15, 78, 56, 43,
      62, 98, 83, 108]

and the sum of the upper left NXN(2X2) is 119 + 114 + 56 + 125 = 414.

I have tried the function below:

def seanMatrix(matrix):
    n = len(matrix) // 2
    rows = len(matrix)
    columns = len(matrix[0])

    for i in range(rows):
        for j in range(columns // 2):
            matrix[i][j], matrix[i][columns - j - 1] = matrix[i][columns - j - 1], matrix[i][j]

    for i in range(rows // 2):
        for j in range(columns):
            matrix[i][j], matrix[rows - i - 1][j] = matrix[rows - i - 1][j], matrix[i][j]

    leftSum= 0
    for i in range(n):
        for j in range(n):
            leftSum += matrix[i][j]

    return leftSum

The code is giving me 396 with the same matrix, which is incorrect.

Question: How do I correct the code to produce the correct output? i.e. to be able to get the maximum sum of the upper-left 2 x 2 matrix.

Asked By: micahondiwa

||

Answers:

You’re not even trying to maximize anything, you’re just blindly reversing everything once. Actually you shouldn’t do any reversals.

Here’s what happens when you reverse row i, then column j, then row i again, then column j again:

   j
  ••••••    ••••••    ••••••    ••••••    ••••••
i •A••B•    •B••A•    •C••A•    •A••C•    •B••C•
  •••••• => •••••• => •••••• => •••••• => ••••••
  ••••••    ••••••    ••••••    ••••••    ••••••
  •C••••    •C••••    •B••••    •B••••    •A••••
  ••••••    ••••••    ••••••    ••••••    ••••••

The other values in row i and column j move in the first reversal but get moved back in the second. So overall we only have that 3-cycle of A, B and C. Everything else is where it was at the start.

This is enough to put every orbit’s largest number to the top-left quadrant. What’s an orbit? It’s four positions that can swap places via row/column reversals. Consider your matrix:

[112, 42, 83, 119,
  56, 125, 56, 49,
  15, 78, 101, 43,
  62, 98, 114, 108]

One orbit has the numbers 42, 83, 98, 114. These four can switch places with each other and with no other. With two such 3-cycles, you can move the 114 to the top-left quadrant, without changing any of the other three orbits. And exactly one of an orbit’s four positions is in the top-left quadrant.

So all you have to do is look at all the orbits, find each orbit’s largest number, and report their sum.

A = [[112,  42,  83, 119],
     [ 56, 125,  56,  49],
     [ 15,  78, 101,  43],
     [ 62,  98, 114, 108]]

n = len(A) // 2
print(sum(max(A[i][j], A[i][~j], A[~i][j], A[~i][~j])
          for i in range(n)
          for j in range(n)))

Output (Try it online!):

414

Note about ~: In the context of indices, I call that the backwards indexing operator. Python supports negative indexes: index -1 is the last element, -2 is the second-to-last, etc. And ~0=-1, ~1=-2, etc. So if i indexes the i-th element from the front, then ~i indexes the i-th element from the back. Very convenient. No need to write length-i-1.

Answered By: Kelly Bundy