[アルゴリズム] 最大部分行列 (dp) (詳細コメント)

トピック

正の整数、負の整数、および 0 からなる N × M 行列を指定して、要素の合計が最大となる部分行列を見つけるコードを作成します。

配列 [r1, c1, r2, c2] を返します。ここで、r1 と c1 はそれぞれ部分行列の左上隅の行番号と列番号を表し、r2 と c2 はそれぞれ部分行列の右下隅の行番号と列番号を表します。コーナー。条件を満たす部分行列が複数ある場合は、いずれかを返すことができます。

注: この質問は、本の元の質問からわずかに変更されています。

例:

入力:
[
[-1,0],
[0,-1]
]
出力: [0,1,0,1]
説明: 入力の太字の要素は、出力で表される行列です。

例証します:

  • 1 <= 行列.長さ、行列[0].長さ <= 200

出典: LeetCode
リンク: https://leetcode-cn.com/problems/max-submatrix-lcci

DP

アイデア

2 次元を 1 次元に変換し、最大部分行列を変換して最大部分列合計を見つけ、垂直累積配列を使用して最大部分列合計を見つけます。

  1. 部分行列の行番号の上限を修正し、新しい垂直累積配列を作成します
  2. 部分行列の行番号の下限を修正
  3. 列番号の右境界を移動し、垂直累積配列の最大部分列和を計算します。
  4. 下限をループします
  5. 上限をループします

コード

class Solution {
    
    
    public int[] getMaxMatrix(int[][] matrix) {
    
    
        int n = matrix.length;
        int m = matrix[0].length;
        int max = Integer.MIN_VALUE;

        // 小矩阵元素之和
        int dp = 0;
        // 左边界,也就是左上角点的x坐标
        int start = 0;
        int[] res = new int[4];
        int[] sum;

        // 小矩阵的第一行
        for (int i = 0; i < n; i++) {
    
    
            // sum是纵向累加数组 放在第一层循环当中
            sum = new int[m];
            // 小矩阵的最后一行
            for (int j = i; j < n; j++) {
    
    
                // dp压缩后横向累加数组  start是小矩阵的最左边
                dp = 0;
                start = 0;
                // 当矩阵的上下固定下来 k++表示右列的移动
                for (int k = 0; k < m; k++) {
    
    
                    // 纵向累加数组加一个数字
                    sum[k] += matrix[j][k];
                    // 加上一个列的值
                    // dp+表示矩阵右下表往右了一点,宽度增大了 dp表示压缩过后一行的和,也就是矩阵的和
                    dp += sum[k];
                    // 三层循环 每次都总结一下最大值 max
                    if (max < dp) {
    
    
                        res[0] = i; res[1] = start;
                        res[2] = j; res[3] = k;
                        max = dp;
                    }
                    /*当矩阵的大小也就是dp的值小于0,根据我们在 最大子序和(53题)的基础,
                        动态转移方程是 f(n)=nums[i]+Math.max(0,f(n-1))  懂的都懂
                        这里就是把当前矩阵的元素总和dp与0比较
                        大于则可以不移动左边界 start,随着循环k++移动右边界
                        此时不需要刷新dp的值 而是添加随着右边界移动增加值
                        小于就需要移动左边界 到当前右边界的右边一列 同时刷新dp的值为0,毕竟是重新开始一个矩阵
                        此时 令左边界 start=k+1 下一次循环之后k++,
                        于是左右边界重合,此时无矩阵,矩阵的元素和dp当然是0咯
                    */
                    if (dp < 0) {
    
    
                        dp = 0;
                        start = k + 1;
                    }
                }
            }
        }
        return res;
    }
}

おすすめ

転載: blog.csdn.net/weixin_45177370/article/details/120684109