2010-04-15 2 views
57

Вход: Двумерный массив NxN - Матрица - с положительными и отрицательными элементами.

Выход: Подматрица любого размера, такая, что ее суммирование является максимальным среди всех возможных подматриц.Получение подматрицы с максимальной суммой?

Требование: сложность алгоритма, чтобы быть O (N^3)

История: С помощью Algorithmist, Ларри и модификации алгоритма Kadane, я сумел решить проблема частично, которая определяет только суммирование - ниже в Java.
Ernesto, которому удалось решить остальную часть проблемы, которая определяет границы матрицы, то есть верхние левые, нижние правые углы - ниже в Ruby.

+0

Под «n-мерным» я предполагаю, что вы имеете в виду двумерный. N * N, а не N^n. – Kobi

+0

Да Коби, я имел в виду 2-мерную (матрицу), извините за эту опечатку. – guirgis

+0

Как насчет размера подматрицы? Может ли это быть чем угодно? –

ответ

21

О восстановлении фактической подматрицы, а не только максимальной суммы, вот что я получил. К сожалению у меня нет времени, чтобы перевести свой код на свой Java версии, поэтому я отправляю мой код на Ruby с некоторыми комментариями в ключевых частях

def max_contiguous_submatrix_n3(m) 
    rows = m.count 
    cols = rows ? m.first.count : 0 

    vps = Array.new(rows) 
    for i in 0..rows 
    vps[i] = Array.new(cols, 0) 
    end 

    for j in 0...cols 
    vps[0][j] = m[0][j] 
    for i in 1...rows 
     vps[i][j] = vps[i-1][j] + m[i][j] 
    end 
    end 

    max = [m[0][0],0,0,0,0] # this is the result, stores [max,top,left,bottom,right] 
    # these arrays are used over Kandane 
    sum = Array.new(cols) # obvious sum array used in Kandane 
    pos = Array.new(cols) # keeps track of the beginning position for the max subseq ending in j 

    for i in 0...rows 
    for k in i...rows 
     # Kandane over all columns with the i..k rows 
     sum.fill(0) # clean both the sum and pos arrays for the upcoming kandane 
     pos.fill(0) 
     local_max = 0 # we keep track of the position of the max value over each Kandane's execution 
     # notice that we do not keep track of the max value, but only its position 
     sum[0] = vps[k][0] - (i==0 ? 0 : vps[i-1][0]) 
     for j in 1...cols 
     value = vps[k][j] - (i==0 ? 0 : vps[i-1][j]) 
     if sum[j-1] > 0 
      sum[j] = sum[j-1] + value 
      pos[j] = pos[j-1] 
     else 
      sum[j] = value 
      pos[j] = j 
     end 
     if sum[j] > sum[local_max] 
      local_max = j 
     end 
     end 
     # Kandane ends here 

     # Here's the key thing 
     # If the max value obtained over the past kandane's execution is larger than 
     # the current maximum, then update the max array with sum and bounds 
     if sum[local_max] > max[0] 
     # sum[local_max] is the new max value 
     # the corresponding submatrix goes from rows i..k. 
     # and from columns pos[local_max]..local_max 
     # the array below contains [max_sum,top,left,bottom,right] 
     max = [sum[local_max], i, pos[local_max], k, local_max] 
     end 
    end 
    end 

    return max # return the array with [max_sum,top,left,bottom,right] 
end 

Некоторые примечания для уточнения:

Я использую массив для сохранения всех значений, относящихся к результату. Вы можете просто использовать пять автономных переменных: max, top, left, bottom, right. Это просто проще назначить в одной строке массиву, а затем подпрограмма возвращает массив со всей необходимой информацией.

Если вы скопируете и вставьте этот код в текстовый редактор с поддержкой Ruby, вы, очевидно, поймете его лучше. Надеюсь это поможет!

+0

Привет, Эрнесто, я только что увидел ваш ответ, большое спасибо за усилия. В ближайшее время я рассмотрю вашу реализацию. – guirgis

0

Посмотрите на JAMA package; Я считаю, что это облегчит вашу жизнь.

+0

Спасибо Anax. Это полезный пакет, и я никогда не слышал об этом, но я думаю, что мне нужно использовать стандартный API, это проблема алгоритма. – guirgis

3

С помощью Algorithmist и Ларри и модификации алгоритма Kadane, вот мое решение:

int dim = matrix.length; 
    //computing the vertical prefix sum for columns 
    int[][] ps = new int[dim][dim]; 
    for (int i = 0; i < dim; i++) { 
     for (int j = 0; j < dim; j++) { 
      if (j == 0) { 
       ps[j][i] = matrix[j][i]; 
      } else { 
       ps[j][i] = matrix[j][i] + ps[j - 1][i]; 
      } 
     } 
    } 
    int maxSoFar = 0; 
    int min , subMatrix; 
    //iterate over the possible combinations applying Kadane's Alg. 
    for (int i = 0; i < dim; i++) { 
     for (int j = i; j < dim; j++) { 
      min = 0; 
      subMatrix = 0; 
      for (int k = 0; k < dim; k++) { 
       if (i == 0) { 
        subMatrix += ps[j][k]; 
       } else { 
        subMatrix += ps[j][k] - ps[i - 1 ][k]; 
       } 
       if(subMatrix < min){ 
        min = subMatrix; 
       } 
       if((subMatrix - min) > maxSoFar){ 
        maxSoFar = subMatrix - min; 
       }      
      } 
     } 
    } 

Единственное, что осталось, чтобы определить элементы подматрицы, а именно: верхний левый и нижний правый угол подматрицы. Любое предложение?

+1

Просто отследите его в своих операторах if. Кстати, вероятно, лучше отредактировать исходный вопрос, а не подавать ответ. – Larry

+0

Мне удалось сделать это в одномерной задаче: для (int i = 0; i лучший) { длина ++; best = subArray - min; } } Но у меня были некоторые проблемы в матричном корпусе. Извините, что я новичок здесь, я не знаю, что лучше. – guirgis

+0

Ну, если вы храните переменную смещения, вы уже знаете i, j и k, чтобы вы могли разобраться с углами подматрицы. – Larry

7

Вот версия Java реализации Эрнесто с некоторыми изменениями:

public int[][] findMaximumSubMatrix(int[][] matrix){ 
    int dim = matrix.length; 
    //computing the vertical prefix sum for columns 
    int[][] ps = new int[dim][dim]; 
    for (int i = 0; i < dim; i++) { 
     for (int j = 0; j < dim; j++) { 
      if (j == 0) { 
       ps[j][i] = matrix[j][i]; 
      } else { 
       ps[j][i] = matrix[j][i] + ps[j - 1][i]; 
      } 
     } 
    } 

    int maxSum = matrix[0][0]; 
    int top = 0, left = 0, bottom = 0, right = 0; 

    //Auxiliary variables 
    int[] sum = new int[dim]; 
    int[] pos = new int[dim]; 
    int localMax;       

    for (int i = 0; i < dim; i++) { 
     for (int k = i; k < dim; k++) { 
      // Kandane over all columns with the i..k rows 
      reset(sum); 
      reset(pos); 
      localMax = 0; 
      //we keep track of the position of the max value over each Kandane's execution 
      // notice that we do not keep track of the max value, but only its position 
      sum[0] = ps[k][0] - (i==0 ? 0 : ps[i-1][0]); 
      for (int j = 1; j < dim; j++) {      
       if (sum[j-1] > 0){ 
        sum[j] = sum[j-1] + ps[k][j] - (i==0 ? 0 : ps[i-1][j]); 
        pos[j] = pos[j-1]; 
       }else{ 
        sum[j] = ps[k][j] - (i==0 ? 0 : ps[i-1][j]); 
        pos[j] = j; 
       } 
       if (sum[j] > sum[localMax]){ 
        localMax = j; 
       } 
      }//Kandane ends here 

      if (sum[localMax] > maxSum){ 
        /* sum[localMax] is the new max value 
        the corresponding submatrix goes from rows i..k. 
        and from columns pos[localMax]..localMax 
        */ 
       maxSum = sum[localMax]; 
       top = i; 
       left = pos[localMax]; 
       bottom = k; 
       right = localMax; 
      }  
     } 
    } 
    System.out.println("Max SubMatrix determinant = " + maxSum); 
    //composing the required matrix 
    int[][] output = new int[bottom - top + 1][right - left + 1]; 
    for(int i = top, k = 0; i <= bottom; i++, k++){ 
     for(int j = left, l = 0; j <= right ; j++, l++){     
      output[k][l] = matrix[i][j]; 
     } 
    } 
    return output; 
} 

private void reset(int[] a) { 
    for (int index = 0; index < a.length; index++) { 
     a[index] = 0; 
    } 
} 
0

Здесь C# решение. Ref: http://www.algorithmist.com/index.php/UVa_108

public static MaxSumMatrix FindMaxSumSubmatrix(int[,] inMtrx) 
{ 
    MaxSumMatrix maxSumMtrx = new MaxSumMatrix(); 

    // Step 1. Create SumMatrix - do the cumulative columnar summation 
    // S[i,j] = S[i-1,j]+ inMtrx[i-1,j]; 
    int m = inMtrx.GetUpperBound(0) + 2; 
    int n = inMtrx.GetUpperBound(1)+1; 
    int[,] sumMatrix = new int[m, n]; 

    for (int i = 1; i < m; i++) 
    { 
     for (int j = 0; j < n; j++) 
     { 
      sumMatrix[i, j] = sumMatrix[i - 1, j] + inMtrx[i - 1, j]; 
     } 
    } 

    PrintMatrix(sumMatrix); 

    // Step 2. Create rowSpans starting each rowIdx. For these row spans, create a 1-D array r_ij    
    for (int x = 0; x < n; x++) 
    { 
     for (int y = x; y < n; y++) 
     { 
      int[] r_ij = new int[n]; 

      for (int k = 0; k < n; k++) 
      { 
       r_ij[k] = sumMatrix[y + 1,k] - sumMatrix[x, k]; 
      } 

      // Step 3. Find MaxSubarray of this r_ij. If the sum is greater than the last recorded sum => 
      //   capture Sum, colStartIdx, ColEndIdx. 
      //   capture current x as rowTopIdx, y as rowBottomIdx. 
      MaxSum currMaxSum = KadanesAlgo.FindMaxSumSubarray(r_ij); 

      if (currMaxSum.maxSum > maxSumMtrx.sum) 
      { 
       maxSumMtrx.sum = currMaxSum.maxSum; 
       maxSumMtrx.colStart = currMaxSum.maxStartIdx; 
       maxSumMtrx.colEnd = currMaxSum.maxEndIdx; 
       maxSumMtrx.rowStart = x; 
       maxSumMtrx.rowEnd = y; 
      } 
     } 
    } 

    return maxSumMtrx; 
} 

public static void PrintMatrix(int[,] matrix) 
{ 
    int endRow = matrix.GetUpperBound(0); 
    int endCol = matrix.GetUpperBound(1); 
    PrintMatrix(matrix, 0, endRow, 0, endCol); 
} 

public static void PrintMatrix(int[,] matrix, int startRow, int endRow, int startCol, int endCol) 
{ 
    StringBuilder sb = new StringBuilder(); 
    for (int i = startRow; i <= endRow; i++) 
    { 
     sb.Append(Environment.NewLine); 
     for (int j = startCol; j <= endCol; j++) 
     { 
      sb.Append(string.Format("{0} ", matrix[i,j])); 
     } 
    } 

    Console.WriteLine(sb.ToString()); 
} 

// Given an NxN matrix of positive and negative integers, write code to find the sub-matrix with the largest possible sum 
public static MaxSum FindMaxSumSubarray(int[] inArr) 
{ 
    int currMax = 0; 
    int currStartIndex = 0; 
    // initialize maxSum to -infinity, maxStart and maxEnd idx to 0. 

    MaxSum mx = new MaxSum(int.MinValue, 0, 0); 

    // travers through the array 
    for (int currEndIndex = 0; currEndIndex < inArr.Length; currEndIndex++) 
    { 
     // add element value to the current max. 
     currMax += inArr[currEndIndex]; 

     // if current max is more that the last maxSum calculated, set the maxSum and its idx 
     if (currMax > mx.maxSum) 
     { 
      mx.maxSum = currMax; 
      mx.maxStartIdx = currStartIndex; 
      mx.maxEndIdx = currEndIndex; 
     } 

     if (currMax < 0) // if currMax is -ve, change it back to 0 
     { 
      currMax = 0; 
      currStartIndex = currEndIndex + 1; 
     } 
    } 

    return mx; 
} 

struct MaxSum 
{ 
    public int maxSum; 
    public int maxStartIdx; 
    public int maxEndIdx; 

    public MaxSum(int mxSum, int mxStart, int mxEnd) 
    { 
     this.maxSum = mxSum; 
     this.maxStartIdx = mxStart; 
     this.maxEndIdx = mxEnd; 
    } 
} 

class MaxSumMatrix 
{ 
    public int sum = int.MinValue; 
    public int rowStart = -1; 
    public int rowEnd = -1; 
    public int colStart = -1; 
    public int colEnd = -1; 
} 
8

Есть уже много ответов, но вот еще одна реализация Java я написал.Он сравнивает 3 решения:

  1. наивных (перебор) - O (N^6) Время
  2. Очевидное ДП решение - O (N^4) времени и O (N^3) пространство
  3. Более умное решение DP на основе алгоритма Кадане - O (n^3) и O (n^2) - пробел

Есть пробелы для n = 10 до n = 70 с шагом 10 с хорошим выход, сравнивающий время выполнения и требования к пространству.

enter image description here

Код:

public class MaxSubarray2D { 

    static int LENGTH; 
    final static int MAX_VAL = 10; 

    public static void main(String[] args) { 

     for (int i = 10; i <= 70; i += 10) { 
      LENGTH = i; 

      int[][] a = new int[LENGTH][LENGTH]; 

      for (int row = 0; row < LENGTH; row++) { 
       for (int col = 0; col < LENGTH; col++) { 
        a[row][col] = (int) (Math.random() * (MAX_VAL + 1)); 
        if (Math.random() > 0.5D) { 
         a[row][col] = -a[row][col]; 
        } 
        //System.out.printf("%4d", a[row][col]); 
       } 
       //System.out.println(); 
      } 
      System.out.println("N = " + LENGTH); 
      System.out.println("-------"); 

      long start, end; 
      start = System.currentTimeMillis(); 
      naiveSolution(a); 
      end = System.currentTimeMillis(); 
      System.out.println(" run time: " + (end - start) + " ms no auxiliary space requirements"); 
      start = System.currentTimeMillis(); 
      dynamicProgammingSolution(a); 
      end = System.currentTimeMillis(); 
      System.out.println(" run time: " + (end - start) + " ms requires auxiliary space for " 
        + ((int) Math.pow(LENGTH, 4)) + " integers"); 
      start = System.currentTimeMillis(); 
      kadane2D(a); 
      end = System.currentTimeMillis(); 
      System.out.println(" run time: " + (end - start) + " ms requires auxiliary space for " + 
        + ((int) Math.pow(LENGTH, 2)) + " integers"); 
      System.out.println(); 
      System.out.println(); 
     } 
    } 

    // O(N^2) !!! 
    public static void kadane2D(int[][] a) { 
     int[][] s = new int[LENGTH + 1][LENGTH]; // [ending row][sum from row zero to ending row] (rows 1-indexed!) 
     for (int r = 0; r < LENGTH + 1; r++) { 
      for (int c = 0; c < LENGTH; c++) { 
       s[r][c] = 0; 
      } 
     } 
     for (int r = 1; r < LENGTH + 1; r++) { 
      for (int c = 0; c < LENGTH; c++) { 
       s[r][c] = s[r - 1][c] + a[r - 1][c]; 
      } 
     } 
     int maxSum = Integer.MIN_VALUE; 
     int maxRowStart = -1; 
     int maxColStart = -1; 
     int maxRowEnd = -1; 
     int maxColEnd = -1; 
     for (int r1 = 1; r1 < LENGTH + 1; r1++) { // rows 1-indexed! 
      for (int r2 = r1; r2 < LENGTH + 1; r2++) { // rows 1-indexed! 
       int[] s1 = new int[LENGTH]; 
       for (int c = 0; c < LENGTH; c++) { 
        s1[c] = s[r2][c] - s[r1 - 1][c]; 
       } 
       int max = 0; 
       int c1 = 0; 
       for (int c = 0; c < LENGTH; c++) { 
        max = s1[c] + max; 
        if (max <= 0) { 
         max = 0; 
         c1 = c + 1; 
        } 
        if (max > maxSum) { 
         maxSum = max; 
         maxRowStart = r1 - 1; 
         maxColStart = c1; 
         maxRowEnd = r2 - 1; 
         maxColEnd = c; 
        } 
       } 
      } 
     } 

     System.out.print("KADANE SOLUTION | Max sum: " + maxSum); 
     System.out.print(" Start: (" + maxRowStart + ", " + maxColStart + 
       ") End: (" + maxRowEnd + ", " + maxColEnd + ")"); 
    } 

    // O(N^4) !!! 
    public static void dynamicProgammingSolution(int[][] a) { 
     int[][][][] dynTable = new int[LENGTH][LENGTH][LENGTH + 1][LENGTH + 1]; // [row][col][height][width] 
     int maxSum = Integer.MIN_VALUE; 
     int maxRowStart = -1; 
     int maxColStart = -1; 
     int maxRowEnd = -1; 
     int maxColEnd = -1; 

     for (int r = 0; r < LENGTH; r++) { 
      for (int c = 0; c < LENGTH; c++) { 
       for (int h = 0; h < LENGTH + 1; h++) { 
        for (int w = 0; w < LENGTH + 1; w++) { 
         dynTable[r][c][h][w] = 0; 
        } 
       } 
      } 
     } 

     for (int r = 0; r < LENGTH; r++) { 
      for (int c = 0; c < LENGTH; c++) { 
       for (int h = 1; h <= LENGTH - r; h++) { 
        int rowTotal = 0; 
        for (int w = 1; w <= LENGTH - c; w++) { 
         rowTotal += a[r + h - 1][c + w - 1]; 
         dynTable[r][c][h][w] = rowTotal + dynTable[r][c][h - 1][w]; 
        } 
       } 
      } 
     } 

     for (int r = 0; r < LENGTH; r++) { 
      for (int c = 0; c < LENGTH; c++) { 
       for (int h = 0; h < LENGTH + 1; h++) { 
        for (int w = 0; w < LENGTH + 1; w++) { 
         if (dynTable[r][c][h][w] > maxSum) { 
          maxSum = dynTable[r][c][h][w]; 
          maxRowStart = r; 
          maxColStart = c; 
          maxRowEnd = r + h - 1; 
          maxColEnd = c + w - 1; 
         } 
        } 
       } 
      } 
     } 

     System.out.print(" DP SOLUTION | Max sum: " + maxSum); 
     System.out.print(" Start: (" + maxRowStart + ", " + maxColStart + 
       ") End: (" + maxRowEnd + ", " + maxColEnd + ")"); 
    } 


    // O(N^6) !!! 
    public static void naiveSolution(int[][] a) { 
     int maxSum = Integer.MIN_VALUE; 
     int maxRowStart = -1; 
     int maxColStart = -1; 
     int maxRowEnd = -1; 
     int maxColEnd = -1; 

     for (int rowStart = 0; rowStart < LENGTH; rowStart++) { 
      for (int colStart = 0; colStart < LENGTH; colStart++) { 
       for (int rowEnd = 0; rowEnd < LENGTH; rowEnd++) { 
        for (int colEnd = 0; colEnd < LENGTH; colEnd++) { 
         int sum = 0; 
         for (int row = rowStart; row <= rowEnd; row++) { 
          for (int col = colStart; col <= colEnd; col++) { 
           sum += a[row][col]; 
          } 
         } 
         if (sum > maxSum) { 
          maxSum = sum; 
          maxRowStart = rowStart; 
          maxColStart = colStart; 
          maxRowEnd = rowEnd; 
          maxColEnd = colEnd; 
         } 
        } 
       } 
      } 
     } 

     System.out.print(" NAIVE SOLUTION | Max sum: " + maxSum); 
     System.out.print(" Start: (" + maxRowStart + ", " + maxColStart + 
       ") End: (" + maxRowEnd + ", " + maxColEnd + ")"); 
    } 

} 
-2

Вот мое решение. Это O (n^3) во времени и O (n^2). https://gist.github.com/toliuweijing/6097144

// 0th O(n) on all candidate bottoms @B. 
// 1th O(n) on candidate tops @T. 
// 2th O(n) on finding the maximum @left/@right match. 
int maxRect(vector<vector<int> >& mat) { 
    int n    = mat.size(); 
    vector<vector<int> >& colSum = mat; 

    for (int i = 1 ; i < n ; ++i) 
    for (int j = 0 ; j < n ; ++j) 
     colSum[i][j] += colSum[i-1][j]; 

    int optrect = 0; 
    for (int b = 0 ; b < n ; ++b) { 
     for (int t = 0 ; t <= b ; ++t) { 
      int minLeft = 0; 
      int rowSum[n]; 
      for (int i = 0 ; i < n ; ++i) { 
       int col = t == 0 ? colSum[b][i] : colSum[b][i] - colSum[t-1][i]; 
       rowSum[i] = i == 0? col : col + rowSum[i-1]; 
       optrect = max(optrect, rowSum[i] - minLeft); 
       minLeft = min(minLeft, rowSum[i]); 
      } 
     } 
    } 

    return optrect; 
} 
38

Вот объяснение того, чтобы пойти с публикуемым кодом. Для эффективного выполнения этой работы есть два ключевых метода: (I) алгоритм Кандана и (II) с использованием префиксных сумм. Вам также необходимо (III) применить трюки к матрице.

Часть I: алгоритм Kandane в

алгоритм Kandane является способом найти непрерывную подпоследовательность с максимальной суммой. Давайте начнем с подхода грубой силы для нахождения максимальной непрерывной подпоследовательности, а затем рассмотрим ее оптимизацию для получения алгоритма Кандана.

Предположим, у вас есть последовательность:

-1, 2, 3, -2 

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

At index 0, we consider appending the -1 
-1, 2, 3, -2 
^ 
Possible subsequences: 
-1 [sum -1] 

At index 1, we consider appending the 2 
-1, 2, 3, -2 
    ^
Possible subsequences: 
-1 (end)  [sum -1] 
-1, 2  [sum 1] 
2   [sum 2] 

At index 2, we consider appending the 3 
-1, 2, 3, -2 
     ^
Possible subsequences: 
-1, (end)  [sum -1] 
-1, 2 (end) [sum -1] 
2 (end)  [sum 2] 
-1, 2, 3  [sum 4] 
2, 3   [sum 5] 
3    [sum 3] 

At index 3, we consider appending the -2 
-1, 2, 3, -2 
      ^
Possible subsequences: 
-1, (end)   [sum -1] 
-1, 2 (end)  [sum 1] 
2 (end)   [sum 2] 
-1, 2 3 (end) [sum 4] 
2, 3 (end)  [sum 5] 
3, (end)   [sum 3] 
-1, 2, 3, -2  [sum 2] 
2, 3, -2   [sum 3] 
3, -2    [sum 1] 
-2     [sum -2] 

Для этого грубой силы подход, мы, наконец, выбрать список с лучшей суммой, (2, 3), и это ответ. Однако, чтобы сделать это эффективным, считайте, что вам действительно не нужно хранить каждый из списков. Из списков, которые еще не закончились, вам нужно только сохранить лучший, другие не могут сделать лучше. Из списков, которые закончились, вам может понадобиться только сохранить лучший, и только если он лучше, чем те, которые еще не закончились.

Итак, вы можете отслеживать, что вам нужно, только с помощью массива позиций и массива сумм. Массив позиции определяется следующим образом: position[r] = s отслеживает список, который заканчивается на r и начинается с s. И, sum[r] дает сумму для подпоследовательности, заканчивающейся на index r. Это оптимизированный подход - алгоритм Кандана.

Запуск через пример снова отслеживании нашего прогресса так:

At index 0, we consider appending the -1 
-1, 2, 3, -2 
^ 
We start a new subsequence for the first element. 
position[0] = 0 
sum[0] = -1 

At index 1, we consider appending the 2 
-1, 2, 3, -2 
    ^
We choose to start a new subsequence because that gives a higher sum than extending. 
position[0] = 0  sum[0] = -1 
position[1] = 1  sum[1] = 2 


At index 2, we consider appending the 3 
-1, 2, 3, -2 
     ^
We choose to extend a subsequence because that gives a higher sum than starting a new one. 
position[0] = 0  sum[0] = -1 
position[1] = 1  sum[1] = 2 
position[2] = 1  sum[2] = 5 

Again, we choose to extend because that gives a higher sum that starting a new one. 
-1, 2, 3, -2 
      ^
position[0] = 0  sum[0] = -1 
position[1] = 1  sum[1] = 2 
position[2] = 1  sum[2] = 5 
positions[3] = 3  sum[3] = 3 

Опять же, лучшая сумма 5 и список из индекса 1 к индексу 2, что (2, 3).

Часть II: Приставка суммирует

Мы хотим, чтобы иметь возможность вычислить сумму по счету, для любой начальной точки до любой конечной точки. Я хочу вычислить эту сумму в O (1) раз, а не просто добавлять, что принимает O (m) время, где m - количество элементов в сумме. С некоторыми предварительными вычислениями это может быть достигнуто. Вот как. Предположим, у вас есть матрица:

a d g 
b e h 
c f i 

Вы можете предвычисления эту матрицу:

a  d  g 
a+b d+e g+h 
a+b+c d+e+f g+h+i 

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

Часть III: Приведение трюки вместе, чтобы найти Макса подматрица

Предположим, что вы знаете, верхний и нижний ряд максимальной подматрицы. Вы могли бы сделать это:

  1. Игнорировать строки выше верхнего ряда и игнорировать ряды внизу внизу. строка.
  2. С какой матрицей осталось считать сумму использования каждого столбца в , чтобы сформировать последовательность (вроде строки, которая представляет несколько строк). (Вы можете быстро вычислить любой элемент этой последовательности с использованием префикса .)
  3. Используйте подход Кандана, чтобы найти лучшую подпоследовательность в этой последовательности . Вы получите указатели слева и справа позиции лучшей подматрицы.

А как насчет фактического определения верхнего и нижнего ряда? Просто попробуйте все возможности. Попытайтесь положить верх в любом месте, где можете, и поместив его в любом месте, и запустите процедуру Kandane-base, описанную ранее для каждой возможности. Когда вы находите максимальное значение, вы отслеживаете верхнее и нижнее положение.

Поиск строки и столбца принимает O (M^2), где M - количество строк. Поиск столбца принимает O (N) время, где N - количество столбцов. Таким образом, общее время равно O (M^2 * N). И, если M = N, требуется время O (N^3).

+2

Привет, Приятное объяснение, однако, проясните следующую строку в Часть 2 - Префикс Sum - «Как только это будет сделано, вы можете получить сумму, идущую вдоль любого столбца, от любой начальной до конечной точки в столбце, просто вычитая два значения." Я понял, что мы можем получить сумму между любыми двумя коллами, вычитая пару значений в новой матрице .. но как сделать эту пару .. ?? Или я ошибаюсь .. ?? –

+0

спасибо, большое объяснение! –

+0

Трюк с префиксом - классная идея! Просто убедитесь, что в проблемах масштаба вы не переполняете любой тип данных, который используете, добавляя так много! – user3076399

1

Я собираюсь опубликовать ответ здесь и могу добавить фактический код C++, если он запрошен, потому что я недавно работал над этим. Некоторые слухи о разрыве и завоевании, которые могут решить это в O (N^2), есть, но я не видел никакого кода, чтобы поддержать это. По моему опыту, вот что я нашел.

O(i^3j^3) -- naive brute force method 
    o(i^2j^2) -- dynamic programming with memoization 
    O(i^2j) -- using max contiguous sub sequence for an array 


if (i == j) 
O(n^6) -- naive 
O(n^4) -- dynamic programming 
O(n^3) -- max contiguous sub sequence 
0

Это моя реализация алгоритма 2D Кадане. Я думаю, что это более понятно. Концепция основана на просто алгоритме kadane. Первый и второй петли основной части (то есть в нижней части кода) состоит в том, чтобы выбрать каждую комбинацию строк, а третий цикл - использовать алгоритм 1D kadane по каждой следующей сумме столбцов (которая может быть вычислена в const time, потому что предварительной обработки матрицы путем вычитания значений из двух выбранных (из комбинаций) строк). Вот код:

int [][] m = { 
      {1,-5,-5}, 
      {1,3,-5}, 
      {1,3,-5} 
    }; 
    int N = m.length; 

    // summing columns to be able to count sum between two rows in some column in const time 
    for (int i=0; i<N; ++i) 
     m[0][i] = m[0][i]; 
    for (int j=1; j<N; ++j) 
     for (int i=0; i<N; ++i) 
      m[j][i] = m[j][i] + m[j-1][i]; 

    int total_max = 0, sum; 
    for (int i=0; i<N; ++i) { 
     for (int k=i; k<N; ++k) { //for each combination of rows 
      sum = 0; 
      for (int j=0; j<N; j++) {  //kadane algorithm for every column 
       sum += i==0 ? m[k][j] : m[k][j] - m[i-1][j]; //for first upper row is exception 
       total_max = Math.max(sum, total_max); 
      } 
     } 
    } 

    System.out.println(total_max); 
-2

Я бы просто разобрать массив NxN удаляющего -ves все, что остается самой высокой суммой к югу матрицы.

Вопрос не говорит о том, что вы должны оставить исходную матрицу неповрежденной или что порядок имеет значение.

+0

Можете ли вы добавить еще ... вещество к своему сообщению? – UmNyobe