题目描述
- 求最大子矩阵问题。
解题方法1
- 首先,我们以每一行做切割统计以当前行作为底的情况下,每个位置往上1的数量(遇到0为止),这样针对每一行我们都可以得到一个数组。如:第一行数组为 1 0 1 1,第二行数组为 2 1 2 2,第三行数组为 3 2 3 0。
- 而针对每一行得到的那个数组,我们可以看做一个条形统计图,然后在这个条形统计图中找到的最大矩形的面积就是每一行对应的最大子矩阵中元素数量。最后依次进行比较就可以得到全局的最大子矩阵。
- 如下为 3 2 3 0对应的条形统计图
- 因此原题可以拆分为两个子问题。1 求出以每一行作为底得到的那个表示条形统计图的数组,2 根据条形统计图求出对应最大子矩阵的面积。 最后将两个问题的结果整合一下得到全局最大子矩阵。
- 对于问题一比较简单,遍历每一行,对于第一行的目标数组就是第一行的各元素,对于第二行的目标数组,如果当前元素为0说明目标数组对应值为0,如果当前元素为1说明目标数组对应值为上一行目标数组对应值加一。
- 问题一代码如下
public static int getmax(int [][] arr){
int[] pre = new int[arr[0].length];
for(int i=0;i<arr.length;i++){
int[] line = arr[i];
if(i==0){
for(int j=0;j<line.length;j++){
pre[j] = line[j];
}
}
else{
for(int j=0;j<line.length;j++){
if(line[j]==0){
pre[j] = 0;
}
else{
pre[j] = pre[j]+1;
}
}
}
for(int a:pre){
System.out.print(a+" ");
}
System.out.println();
}
return 0;
}
- 对于问题二,如何才能快速高效计算出条形统计图的最大矩形,起初的设想是用最短的一列乘以总列数,但是这是不对的,比如:2 3 2 1的最大矩形面积是6而不是4。
- 我们可以扫描每一列,对于每一列进行左右扫描直到遇到更短的列扫描结束,然后用列长乘以扫描列数就是每一列对应的矩形面积。比如2 3 2 1,第一列扫描面积为23=6,第二列31=3,第三列23=6,第四列14=4。
- 这个行为的本质就是寻找每一列左边第一个比当前列小的位置在哪里以及右边第一个比当前列小的位置在哪里,具体应该如何扫描才能使得时间复杂度最小呢?可以使用栈。
- 以 3 4 5 4 3 6为例,我们使用栈来存放元素的下标,保证栈从栈底到栈顶是递增的。遇到值比当前栈顶大的直接入栈,否则将栈顶出栈直到当前值大于栈顶再入栈。
- 出入栈的规则如下:
3的下标0入栈,4的下标1入栈,5的下标2入栈,5的下标2出栈,4的下标2出栈,新的4的下标3入栈… - 明确出栈和入栈规则后我们就可以针对每一列计算它左右的扩展边界,计算每一列的扫描面积。对于每一列的下标,要等到它出栈的那一刻才能明确它的左右边界。
- 所以对于位置j的柱子,它的最大扫描面积为 (i-1-(k+1)+1)乘上当前柱子的高度。
- 下面编写问题二的代码,需要注意的是当j出栈后栈为空时说明目前为止j的元素最小那么j的左边界为0,如果j的元素值与i的元素值相等,就按i-1为右边界计算不影响最终结果。
public static int getmaxline(int [] height){
Stack<Integer> s = new Stack<>();
int maxscan = 0;
for(int i=0;i<height.length;i++){
if(s.empty() || height[i]>height[s.peek()]){
s.push(i);
}
else{
while(!s.empty() && height[i]<=height[s.peek()]){
int j =s.pop();
int k = s.empty()?-1:s.peek();
int left = k+1;
int right = i-1;
int scan = (right-left+1)*height[j];
if(scan>maxscan){
maxscan = scan;
}
}
s.push(i);
}
}
while(!s.empty()){
int j =s.pop();
int k = s.empty()?-1:s.peek();
int left = k+1;
int right = height.length-1;
int scan = (right-left+1)*height[j];
if(scan>maxscan){
maxscan = scan;
}
}
return maxscan;
}
public class Test {
public static void main(String[] args) {
int[][] arr = {
{1,0,1,1},
{1,1,1,1},
{1,1,1,0}
};
int max = getmaxmap(arr);
System.out.println(max);
}
public static int getmaxline(int [] height){
Stack<Integer> s = new Stack<>();
int maxscan = 0;
for(int i=0;i<height.length;i++){
if(s.empty() || height[i]>height[s.peek()]){
s.push(i);
}
else{
while(!s.empty() && height[i]<=height[s.peek()]){
int j =s.pop();
int k = s.empty()?-1:s.peek();
int left = k+1;
int right = i-1;
int scan = (right-left+1)*height[j];
if(scan>maxscan){
maxscan = scan;
}
}
s.push(i);
}
}
while(!s.empty()){
int j =s.pop();
int k = s.empty()?-1:s.peek();
int left = k+1;
int right = height.length-1;
int scan = (right-left+1)*height[j];
if(scan>maxscan){
maxscan = scan;
}
}
return maxscan;
}
public static int getmaxmap(int [][] arr){
int[] pre = new int[arr[0].length];
int max = 0;
for(int i=0;i<arr.length;i++){
int[] line = arr[i];
if(i==0){
for(int j=0;j<line.length;j++){
pre[j] = line[j];
}
}
else{
for(int j=0;j<line.length;j++){
if(line[j]==0){
pre[j] = 0;
}
else{
pre[j] = pre[j]+1;
}
}
}
int s = getmaxline(pre);
max = max<s?s:max;
}
return max;
}
}