题目描述

有一个正整数和负整数组成的NxN矩阵,请编写代码找出元素总和最大的子矩阵。请尝试使用一个高效算法。

给定一个int矩阵mat和矩阵的阶数n,请返回元素总和最大的子矩阵的元素之和。保证元素绝对值小于等于100000,且矩阵阶数小于等于200。

测试样例:

[[1,2,-3],[3,4,-5],[-5,-6,-7]],3


返回:10

/*思路:
1.求子矩阵的最大和,首先得判定所有可能的子矩阵(纵向选定)
2.纵向选定后,将这块子矩阵按行累加压缩成一维,然后处理简单的一维(横向选定)
3.过程中有最大值出现即时更新即可
*/
public class SubMatrix {
    public int sumOfSubMatrix(int[][] mat, int n) {        
        int maxSum = Integer.MIN_VALUE;
        int sum[] = new int[mat[0].length];
        for(int i=0; i<mat.length; i++){//压缩的起点
            for(int j=0; j<sum.length; j++) sum[j]=0;//每次更换起点sum就重置,感觉还可改进
            for(int t=0; t<mat.length-i; t++){//步长,矩阵=起点+步长
            	for(int j=0; j<sum.length; j++) sum[j]+=mat[i+t][j];//求上述矩阵的压缩和
                maxSum = Math.max(maxSum, getMaxSum(sum)); //压缩为一维求和,取最大值               
            }       
        }        
        return maxSum;
    }
    
    public int getMaxSum(int a[]){//常用,一维数组中连续子数组的最大和int maxSum = Integer.MIN_VALUE;
        int curSum = 0;
        for(int i=0; i<a.length; i++){
            curSum += a[i];
            maxSum = Math.max(maxSum, curSum);
            if(curSum<0) curSum=0;
        }
        return maxSum;
    }    
}



 

或者:

public int maxSubMatrix(int[][] mat, int n) {
		// write code here
		/*
		 * 法1.穷举
		 * 法2.贪心法
		 * 首先设置子矩阵的列数,得到二维矩阵row[][],
		 * 其中row[i][j]表示第i行的第j列开始到j+row_len-1列的加和
		 * 对row[i][j]的每一列进行一维数组的最大序列和的计算,复杂度O(n)
		 * 最大序列和计算方式见q17_8
		 * 因此最终复杂度为0(n^3)
		 */
		int max = 0;
		for(int row_len = n; row_len >= 1; row_len--){
			int[][] row = new int[n][n-row_len+1];
			for(int i = 0; i <= n-1; i++){
				for(int j = 0; j<= n-row_len; j++){
					row[i][j] = cal(mat, j, i, row_len);
				}
			}
			//求每个一维子矩阵中的最大序列和
			for(int i = 0; i < n-row_len+1; i++){
				int sum = 0;
				for(int j = 0; j < n; j++){
					sum += row[j][i];
					if(sum < 0){
						sum = 0;
					}else if(sum > max){
						max = sum;
					}
				}
			}
		}
		return max;
	}
    private int cal(int[][] mat, int start, int row_num, int len){
		int cols = mat[0].length;
		int sum = 0;
		for(int i = start; i <= start+len-1; i++){
			if(i < cols){
				sum += mat[row_num][i];
			}
		}
		return sum;
	}

 

问题描述:

给定一个由整数组成二维矩阵(r*c),现在需要找出它的一个子矩阵,使得这个子矩阵内的所有元素之和最大,并把这个子矩阵称为最大子矩阵。
例子:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
其最大子矩阵为:

9 2
-4 1
-1 8
其元素总和为15。

 

假设最大子矩阵的结果为从第r行到k行、从第i列到j列的子矩阵,如下所示(ari表示a[r][i],假设数组下标从1开始):

| a11 …… a1i ……a1j ……a1n |
   | a21 …… a2i ……a2j ……a2n |
   |  .     .     .    .    .     .    .   |
   |  .     .     .    .    .     .    .   |
   | ar1 …… ari ……arj ……arn |
   |  .     .     .    .    .     .    .   |
   |  .     .     .    .    .     .    .   |
   | ak1 …… aki ……akj ……akn |
   |  .     .     .    .    .     .    .   |
   | an1 …… ani ……anj ……ann |

 那么我们将从第r行到第k行的每一行中相同列的加起来,可以得到一个一维数组如下:
 (ar1+……+ak1, ar2+……+ak2, ……,arn+……+akn)
 由此我们可以看出最后所求的就是此一维数组的最大子断和问题,到此我们已经将问题转化为上面的已经解决了的问题了。

1、如果没有最大子段和问题的基础,最直接的办法,穷举法,对二维矩阵中所有子矩阵进行计算求得最大值,时间复杂度为(O(n^2*n^2));

2、基于最大子段和问题,算出任意n行的和数组,转变成最大字段和进行处理,对于任意n行,如果采用各个处理的话,其时间复杂度相对较高,所以对和数组的处理是本题的又一关键;

 

3、压缩矩阵

 

下面举一个简单的例子。在一个一维的数列中,要想求从第i个元素到第j个元素的和,我们可以用这样的方法:设数组sum[i]表示从第1个到第i个元素的和,则:求从第i个元素到第j个元素的和,只需用sum[j]-sum[i]就足够了。由此推广到二维矩阵,设sum[i,j]表示矩阵第j列前i个元素的和,cost[i,j]表示原始数据,则:

压缩数据程序代码为:

for i:=1 to n do

  for j:=1 to m do

    sum[i,j]:=sum[i-1,j]+cost[i,j];

下一个问题是,如何将数据从压缩的数组中读出。

读取数据代码为:

for i:=0 to n-1 do

  for j:=i+1 to n do

for k:=1 to n do temp[k]:=sum[j,k]-sum[i,k];

到此,最大子矩阵问题就完全转换为连续最大和问题。