Прямоугольник максимальной суммы в 2D-матрице с использованием «разделяй и властвуй»

#c #algorithm

Вопрос:

Мне нужно реализовать алгоритм максимальной суммы, который использует стратегию «разделяй и властвуй» на 2D-матрице. Я знаком с подходом грубой силы, который выполняется за O(n^6) времени, и алгоритмом Кадана, который выполняется за O(n^3), но как бы можно было реализовать подход «разделяй и властвуй»? Я думал об этом весь день, и ничего не приходит в голову. Чтобы обеспечить контекст, решение грубой силы заключается в следующем:

 void maxSumBruteForce() {
    vector<vector<int>> matrix = genRandomMatrix();
    int maxSum = INT_MIN;
    printMatrix(matrix);

    for (int startRow = 0; startRow < matrix.size();   startRow) {
        for (int startCol = 0; startCol < matrix.size();   startCol) {
            for (int endRow = startRow; endRow < matrix.size();   endRow) {
                for (int endCol = startCol; endCol < matrix.size();   endCol) {
                    int curSum = 0;
                    for (int row = startRow; row <= endRow;   row) {
                        for (int col = startCol; col <= endCol;   col) {
                            curSum  = matrix[row][col];
                        }
                    }

                    if (curSum > maxSum)
                        maxSum = curSum;
                }
            }
        }
    }

    cout << "MAX SUM: " << maxSum << endl;
}
 

Алгоритм Кадана можно найти здесь: https://www.geeksforgeeks.org/maximum-sum-rectangle-in-a-2d-matrix-dp-27/

Я полагаю, что решение проверит четыре квадранта матрицы, а затем различные комбинации середин.

Комментарии:

1. Тактическое примечание: Вундеркинды Для вундеркиндов практически не курируют контент, поэтому вам действительно нужно быть осторожным, когда вы получаете советы с сайта. Если вы уже не знаете большую часть того, что ищете, вы, возможно, не сможете отличить правильную работу гения от веселых разглагольствований кого-то, кто настолько неправ, что не может понять, что он не прав . Я видел более дикие произведения фэнтези о Гиках для гиков, чем за 40 лет игры в Damp;D.

2. Я понимаю, к чему вы клоните, но я видел несколько сайтов, подтверждающих решение, реализованное с помощью алгоритма Кадана. Я до сих пор не смог добиться какого-либо прогресса в решении этой проблемы.

3. Кадане можно переформулировать как разделяй и властвуй: personal.utdallas.edu/~daescu/maxsa.pdf .

4. Подобные проблемы, как правило, очень эффективно решаются с помощью методов динамического программирования.

5. @DavidEisenstat Я видел, как это использовалось для задачи максимального подмножества, но моя проблема заключается в том, чтобы найти максимальный прямоугольник. Я действительно не знаю, как с этим поступить. Я также понимаю, что обычно динамическое программирование было бы правильным решением, но мне сказали реализовать решение «разделяй и властвуй», а не динамическое программирование.

Ответ №1:

Одним из способов адаптации статьи 1, которой Дэвид Эйзенстат поделился в комментариях, может быть следующее:

Если мы разделим прямоугольник на два,

  -------------------
|         |         |
|         |         |
|   A     |    B    |
|         |         |
|         |         |
 -------------------
 

либо максимальная сумма A B указана, либо она указана, либо она находится в прямоугольнике, который имеет часть A и часть B . Для вычисления последнего мы используем с каждой стороны total_sum , max_sum , max_prefix , max_suffix , для каждой O(num_rows^2) границы строки.

         A
 ---------------
|               |
|-----row_i-----|
|               |
|<--total_sum-->|
|               |
|max_pfx--|     |
|     |--max_sfx|
|               |
|-----row_j-----|
|               |
|               |
 ---------------
 

Затем для каждого окна строк row_i до row_j , из A и B мы имеем:

 total_sum_AB = total_sum_A   total_sum_B
max_prefix_AB = max(max_prefix_A, total_sum_A   max_prefix_B)
max_suffix_AB = max(max_suffix_B, max_suffix_A   total_sum_B)
max_sum_AB = max(max_sum_A, max_sum_B, max_suffix_A   max_prefix_B)
 

Код на Python:

 def g(M, num_rows, l, r):
  dp = [[None] * num_rows for _ in range(num_rows)]

  if l == r:
    for i in range(num_rows):
      dp[i][i] = [M[i][l], M[i][l], M[i][l], M[i][l]]

      for j in range(i 1, num_rows):
        dp[i][j] = [M[j][l]   dp[i][j-1][0], M[j][l]   dp[i][j-1][1], M[j][l]   dp[i][j-1][2], M[j][l]   dp[i][j-1][3]]

    return dp

  mid = l   (r - l) // 2

  dp_l = g(M, num_rows, l, mid)
  dp_r = g(M, num_rows, mid   1, r)

  for i in range(num_rows):
    for j in range(i, num_rows):
      [total_sum_l, max_sum_l, max_pfx_l, max_sfx_l] = dp_l[i][j]
      [total_sum_r, max_sum_r, max_pfx_r, max_sfx_r] = dp_r[i][j]

      total_sum = total_sum_l   total_sum_r
      max_pfx = max(max_pfx_l, total_sum_l   max_pfx_r)
      max_sfx = max(max_sfx_r, total_sum_r   max_sfx_l)
      max_sum = max(max_sum_l, max_sum_r, max_sfx_l   max_pfx_r)

      dp[i][j] = [total_sum, max_sum, max_pfx, max_sfx]

  return dp


def f(M):
  num_rows = len(M)

  dp = g(M, num_rows, 0, len(M[0]) - 1)

  best = -float('inf')

  for i in range(num_rows):
    for j in range(i, num_rows):
      [total_sum, max_sum, max_pfx, max_sfx] = dp[i][j]
      best = max(best, max_sum, max_pfx, max_sfx)

  return best
 

Выход:

 M = [
  [ 1,  2, -1, -4,-20],
  [-8, -3,  4,  2,  1],
  [ 3,  8, 10,  1,  3],
  [-4, -1,  1,  7, -6]
]

print(f(M)) # 29

"""
  [ 1,  2, -1, -4,-20],
       -----------
  [-8,| -3,  4,  2,|  1],
  [ 3,|  8, 10,  1,|  3],
  [-4,| -1,  1,  7,| -6]
       -----------
"""
 

Разделяй и властвуй наносит ответный удар: максимум-подмножество в линейное время 1 (Овидиу Даеску и Шейн Сент-Люс, Факультет компьютерных наук, Техасский университет в Далласе)