1.题目描述
面试题 17.24. 最大子矩阵
给定一个正整数和负整数组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。
返回一个数组 [r1, c1, r2, c2]
,其中 r1
, c1
分别代表子矩阵左上角的行号和列号,r2
, c2
分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。
注意:本题相对书上原题稍作改动
示例:
1 2 3 4 5 6 7
| 输入: [ [-1,0], [0,-1] ] 输出: [0,1,0,1] 解释: 输入中标粗的元素即为输出所表示的矩阵
|
说明:
- 1 <= matrix.length, matrix[0].length <= 200
2.代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| class Solution { public static int[] getMaxMatrix(int[][] matrix) { //矩阵的行数 int row = matrix.length; //矩阵的列数 int col = matrix[0].length;
//最值 int answerMax = Integer.MIN_VALUE;
//位置数组 int[] answerArray = new int[4];
//列上的前缀合 int[][] colPrefix = new int[row][col]; for (int i = 0; i < col; i++) { //第一行各项的前缀合 colPrefix[0][i] = matrix[0][i];
//第二行之后的各项前缀合 for (int j = 1; j < row; j++) { colPrefix[j][i] = colPrefix[j-1][i] + matrix[j][i]; } }
//临时数组,用来保存接下来转化的一维数组 int[] temp = new int[col];
//依次枚举每一种转化的情况 for (int fromRow = 0; fromRow < row; fromRow++) { for (int toRow = fromRow; toRow < row; toRow++) { //fromRow到toRow进行合并 for (int i = 0; i < col; i++) { temp[i] = fromRow == 0 ? colPrefix[toRow][i] : colPrefix[toRow][i] - colPrefix[fromRow-1][i]; }
//求解每一种情况的最大子数组和 int[] maxArrayResult = getMaxArray(temp);
//刷新最大的情况 int max = maxArrayResult[0]; int maxFromCol = maxArrayResult[1]; int maxToCol = maxArrayResult[2]; if (max > answerMax){ answerMax = max;
answerArray[0] = fromRow; answerArray[1] = maxFromCol; answerArray[2] = toRow; answerArray[3] = maxToCol; } } }
return answerArray; }
/** * 求解一维数组的最大子数组和 * @param nums * @return */ private static int[] getMaxArray(int[] nums) { //上一个以当前节点为终结节点的最大子数组和 int preMax = nums[0];
//当前的以当前节点为终结节点的最大子数组和 int max = nums[0];
//真正的起止列数 int realFromCol = 0; int realToCol = 0;
//临时的开始列数 int fromCol = 0;
//枚举 for (int i = 1; i < nums.length; i++) { if (preMax <= 0){ preMax = nums[i];
//如果需要刷新最大值和起止列 if (preMax > max){ max = preMax;
realFromCol = i; realToCol = i; }
//起始列发生了变化 fromCol = i; }else { preMax = preMax + nums[i];
//如果需要刷新最大值 if (preMax > max){ max = preMax;
realFromCol = fromCol; realToCol = i; } } }
return new int[]{max, realFromCol, realToCol}; } }
|
1. 解题思路:
既然做到了这一题,那么我们肯定已经遇到了最大子序和的问题。此题是最大子序和问题的升级版本,从一维升级到了二维。我们主要的解题思路就是将二维问题降为一维问题方便解决。
2. 操作解读:
1> 如何从二维问题降低为一维问题?
- 求出二维数组(矩阵)的列上的前缀合,便于枚举。(代码的 15 ~ 25 行)
- 枚举各种组合,就是某两列的前缀合相减。(代码 30 ~ 36 行)
2> 降低为一维之后,我们求得都是最大值,如何返回范围?
- 首先范围所在的行在枚举情况的时候就已经确定了,范围所在的列通过求解一维数组的函数返回。
- 因为某些原因,导致最大和从一个新的数字开始,那么就需要一个临时的变量来记录起始值的变化,这就是代码(76 行)的作用。