题目
求一个矩阵中最大的二维矩阵(元素和最大);
实现
方法1
思路:遍历每个二维矩阵,记录最大的和;时间复杂度o(mn);
代码:
public static void Max2DMatrix(int[][] A,int row,int col){
int maxSum=0;
int sum=0;
int res_i=0,res_j=0;
for(int i=0;i<row-1;i++){
for(int j=0;j<col-1;j++){
sum=A[i][j]+A[i+1][j]+A[i][j+1]+A[i+1][j+1];
if(maxSum<sum){
maxSum=sum;
res_i=i;
res_j=j;
}
}
}
result[0][0]=A[res_i][res_j];
result[0][1]=A[res_i][res_j+1];
result[1][0]=A[res_i+1][res_j];
result[1][1]=A[res_i+1][res_j+1];
}
方法2
对方法1进行分析,方法1在每次遍历时,必须同时访问4个元素,如图所示:
从图中可以看出,多个元素被重复访问多次。
我们对其进行改进,使每个元素的访问次数尽可能的减少。改进方法如下:
1)增加一个变量last_vsum(最新纵向和),初始化为A[0][0]+A[1][0]
;
2)改变遍历方式,每次遍历只访问纵向的2个元素A[i][j]和A[i+1][j];横向遍历的起始点从第二列开始;
3)改变求和方式,求和方法:将上次保存的last_vsum+A[i][j]+A[i+1][j];last_vsum=A[i][j]+A[i+1][j];
这样可以减少某些元素的访问次数;
代码实现:
public static void Max2DMatrix_2(int[][] A,int row,int col){
int last_vsum=A[0][0]+A[1][0];
int maxSum=0;
int sum=0;
int res_i=0,res_j=0;
for(int i=0;i<row-1;i++){
for(int j=1;j<col;j++){
sum=last_vsum;
last_vsum=A[i][j]+A[i+1][j];
sum+=last_vsum;
if(maxSum<sum){
maxSum=sum;
res_i=i;
res_j=j-1;
}
}
}
result[0][0]=A[res_i][res_j];
result[0][1]=A[res_i][res_j+1];
result[1][0]=A[res_i+1][res_j];
result[1][1]=A[res_i+1][res_j+1];
}
方法2与方法1的时间复杂度相同,但效率高,尤其在大矩阵情况下。但是当矩阵的行数大于列数,方法2效率下降;
===》如果列大于行,使用方法2—横向遍历;
如果列小于行,使用–纵向遍历;
扩展
要求求出最大的子矩阵。
分析:
这是一维最大字串的二维扩展问题。
一维最大字串(最大连续子数组和)问题–动态规划:
b[i]–表示包含a[i]的子数组和;b[i]=max{b[i-1]+a[i],a[i]};
将最大字串应用到该题目中,步骤:
1)压缩列
将每列自上向下做累加,存入b[i][j]中;b[i][j]表示第j列,自第一行到第i行的和;
对于第j列,从第i行到第k行的和:b[k][j]-b[i][j];
2)DP列
得到b后,对b求最大子数组和问题;b是一维的;
然后循环DP,总共三层循环:
最外层i从1-n,表明子矩阵从第i行到第开始累加的;
第二层j从i-n,表明子矩阵从第i行累加到第j行;
第三层k从1到m做DP;
代码实现
public static int maxSubMatrix(int[][] A,int row,int col){
int[] b=new int[col];
int i,j,k;
int max=0,sum=Integer.MIN_VALUE;
for(i=0;i<row;i++){
for(k=0;k<col;k++){
b[k]=0;
}
for(j=i;j<row;j++){//从i行到j行的和;先是第i行(1行);然后从i到i+1(2行),依次求,最后从i到j行的和;每求一次得到一个b的一维数组,然后对该数组求最大连续子数组和(子矩阵的和),判断是否比当前维护的最大子矩阵和大;--从第i行开始依次压缩列,压缩1列/压缩2列...;
for(k=0;k<col;k++){
b[k]+=A[j][k];
}
max=maxSubArray(col,b);
if(max>sum){
sum=max;
}
}
}
return sum;
}
public static int maxSubArray(int n,int[] a){
int b=0;
int sum=Integer.MIN_VALUE;
for(int i=0;i<n;i++){
if(b>0){
b+=a[i];
}else{
b=a[i];
}
if(b>sum){
sum=b;
}
}
return sum;
}