【问题标题】:Getting the submatrix with maximum sum?获得最大和的子矩阵?
【发布时间】:2011-02-08 06:58:59
【问题描述】:

输入:一个二维数组 NxN - 矩阵 - 具有正负元素。

输出:任意大小的子矩阵,其求和是所有可能的子矩阵中的最大值。

要求:算法复杂度为O(N^3)

历史:在算法专家 Larry 和 Kadane 算法的修改的帮助下,我设法部分解决了仅确定总和的问题 - 下面在Java.
感谢 Ernesto 解决了剩下的问题,即确定矩阵的边界,即左上角、右下角 - 在 Ruby 下方。

【问题讨论】:

  • “n 维”我假设您的意思是 2 维。 N*N,不是 N^n。
  • 是的 Kobi,我的意思是二维(矩阵),抱歉这个错字。
  • 子矩阵的大小呢?可以是什么吗?
  • 是的,它可以是任意大小,只要它是子矩阵,可以是矩阵本身,也可以是向量。
  • 这是一个Dynamic Programming 问题,您可以在Algorithmist 阅读有关O(N^3) 解决方案的信息。

标签: algorithm dynamic-programming max submatrix


【解决方案1】:

看看JAMA包;我相信它会让您的生活更轻松。

【讨论】:

  • 谢谢Anax。这是一个有用的包,我从未听说过,但我认为我需要使用标准 API,这有点算法问题。
【解决方案2】:

Algorithmist 和 Larry 的帮助下,以及对 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;
                }                    
            }
        }
    }

唯一剩下的就是确定子矩阵元素,即:子矩阵的左上角和右下角。有人建议吗?

【讨论】:

  • 只需在 if 语句中跟踪它。顺便说一句,与提交答案相比,编辑您的原始问题可能会更好。
  • 我设法在一维问题中做到了这一点: for (int i = 0; i best){ 长度 ++;最好的 = subArray - min;但我在矩阵案例中遇到了一些问题。对不起,我是新手,我不知道什么是最好的。
  • 好吧,如果你存储一个偏移变量,你已经知道 i、j 和 k,所以你可以从中找出子矩阵的角。
  • 感谢拉里的帮助。我知道这是我应该做的,但问题是我无法确定偏移量将在哪里知道“最小”元素坐标,以及如何应用长度值来找到右角。
【解决方案3】:

关于恢复实际子矩阵,而不仅仅是最大和,这就是我得到的。抱歉,我没有时间将我的代码翻译成你的 java 版本,所以我发布了我的 Ruby 代码,其中包含一些关键部分的 cmets

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 Kadane
  sum = Array.new(cols) # obvious sum array used in Kadane
  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
      # Kadane over all columns with the i..k rows
      sum.fill(0) # clean both the sum and pos arrays for the upcoming Kadane
      pos.fill(0)
      local_max = 0 # we keep track of the position of the max value over each Kadane'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
      # Kadane ends here

      # Here's the key thing
      # If the max value obtained over the past Kadane'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 的启用文本突出显示的编辑器中,您显然会更好地理解它。希望这会有所帮助!

【讨论】:

  • 你好 Ernesto,我刚刚看到你的回答,非常感谢你的努力。我很快就会调查你的实现。
【解决方案4】:

这是一个带有一些修改的 Ernesto 实现的 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++) {
            // Kadane 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 Kadane'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;
                }
            }//Kadane 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;
    }
}

【讨论】:

    【解决方案5】:

    这里是 C# 解决方案。参考: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;
    }
    

    【讨论】:

      【解决方案6】:

      已经有很多答案了,但这是我写的另一个 Java 实现。它比较了 3 种解决方案:

      1. 天真(蛮力) - O(n^6) 时间
      2. 显而易见的 DP 解决方案 - O(n^4) 时间和 O(n^3) 空间
      3. 基于 Kadane 算法的更聪明的 DP 解决方案 - O(n^3) 时间和 O(n^2) 空间

      有 n = 10 到 n = 70 的样本运行,增量为 10,比较运行时间和空间要求的输出很好。

      代码:

      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 + ")");
          }
      
      }
      

      【讨论】:

        【解决方案7】:

        这是我的解决方案。它在时间上为 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;
        }
        

        【讨论】:

          【解决方案8】:

          这是与发布的代码一起使用的解释。有两个关键技巧可以有效地完成这项工作:(I)Kadane 算法和(II)使用前缀和。您还需要 (III) 将这些技巧应用于矩阵。

          第一部分:Kadane 算法

          Kadane 的算法是一种找到具有最大和的连续子序列的方法。让我们从寻找最大连续子序列的蛮力方法开始,然后考虑对其进行优化以获得 Kadane 算法。

          假设你有序列:

          -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 结尾的子序列的总和。这是 Kadane 算法的优化方法。

          再次运行示例以这种方式跟踪我们的进度:

          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)。

          第二部分:前缀和

          我们希望有一种方法来计算沿行的总和,对于任何起点到任何终点。我想在 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
          

          完成后,只需减去两个值,您就可以得到从该列中任何起点到终点沿任何列的总和。

          第三部分:汇集技巧以找到最大子矩阵

          假设您知道最大子矩阵的顶行和底行。你可以这样做:

          1. 忽略顶行以上的行并忽略底行以下的行 行。
          2. 剩下的矩阵,考虑使用每列的总和 形成一个序列(有点像代表多行的行)。 (您可以使用前缀快速计算此序列的任何元素 总和方法。)
          3. 使用 Kadane 的方法找出最佳子序列 顺序。你得到的索引会告诉你左右 最佳子矩阵的位置。

          现在,实际计算出顶行和底行怎么样?尝试所有的可能性。尝试将顶部放置在任何可以放置的位置,并将底部放置在任何可以放置的位置,并针对每种可能性运行前面描述的 Kadane-base 程序。当您找到最大值时,您会跟踪顶部和底部位置。

          查找行和列需要 O(M^2),其中 M 是行数。查找列需要 O(N) 时间,其中 N 是列数。所以总时间是 O(M^2 * N)。并且,如果 M=N,则所需时间为 O(N^3)。

          【讨论】:

          • 嗨,很好的解释,但是,请澄清第 2 部分中的以下行 - 前缀总和 - “完成后,您可以从列只需减去两个值。”我知道我们可以通过在新矩阵中减去一对值来获得任何两个列之间的总和..但是如何做那对..??还是我弄错了..??
          • 前缀和技巧是一个很酷的主意!只要确保在规模问题中添加这么多数据不会溢出您正在使用的任何数据类型!
          • 你Kadane的解释真好。但我觉得在你解释的最后一行,这个“positions[3] = 3 sum[3] = 3”实际上应该是这个->“position[3] = 1 sum[3] = 3”。这是因为 sum 是通过与前一个 sum 相加获得的,而不是通过该元素本身获得的。因此,对于索引 3,起始位置应保持为 1。
          【解决方案9】:

          我将在这里发布一个答案,如果需要,我可以添加实际的 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
          

          【讨论】:

            【解决方案10】:

            这是我实现的 2D Kadane 算法。我认为这更清楚。该概念仅基于 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);
            

            【讨论】:

              【解决方案11】:

              我只会解析 NxN 数组,删除 -ves 剩下的就是子矩阵的最高和。

              问题并不是说您必须保持原始矩阵不变或顺序很重要。

              【讨论】:

              • 您能在帖子中添加更多...内容吗?
              猜你喜欢
              • 1970-01-01
              • 2012-10-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-04-05
              相关资源
              最近更新 更多