1.求某个矩阵a[m][n]的子矩阵的最大和
思路:语文表达能力有限,下面举例说明
假设一个矩阵a[4][5]={
{3,4,2,1,-5},
{2,2,5,-6,4},
{-5,2,1,3,3},
{-3,4,-2,-2,3}
}
建立一个辅助数组sum[m*(m+1)/2][n],分别记录遍历行组合的每一列的和:
sum[1][i] = {3,4,2,1,-5}
//若子矩阵被包含于第一行,则其各列累加和为sum[1],此时只需求累加和最大的子数组即可,故子矩阵最大累加和为3+4+2+1=10
sum[2][i] = {5,6,7,-5,-1{}
//若子矩阵被包含于第一行和第二行,则其各列累加和为sum[2],此时子矩阵最大累加和为5+6+7=18
sum[3][i] = {0,8,8,-2,2}
//若子矩阵被包含于第一行、第二行和第三行,则其各列累加和为sum[3],此时子矩阵最大累加和为0+8+8+(-2)+2=16
依次类推,sum[4]表示子矩阵被包含于前四行,sum[5]表示只被包于含第二行,sum[6]表示只被包含于第二行和第三行,等等
即:1,12,123,1234,12345,2,23,234,2345,3,34,345,4,45,5
之后即可在sum中找出累加和最大的子矩阵之和
时间复杂度为O(m²*n),即若m<n时,记录列累加和,若m>n,则辅助数组记录行累加和
2.给定一个无序矩阵,给定一个值k,求累加和小于等于k的最大子矩阵大小,矩阵大小用其中的元素个数来表示
思路:
假设有一个数组,给定一个值k,求累加和小于等于k的最长子数组长度,假设必须以j位置为子数组的结尾位置,从开始位置累加至j位置为sum[j],并且sum[j]>=k,则必定有一个位置i<j,使得sum[i]>=sum[j]-k,(注,此时i位置为最早的sum[i]>=k的位置)则必须以j位置结尾的子数组且和小于等于k的最长长度为j-i。遍历数组即可求解。
举例:
a[] = {3,-1,2,-1,4,-2,5}
sum[] = {3,2,4,3,7,5,9}//累加和数组
max[] = {3,3,4,4,7,7,9}//累加和的区域最大值数组
在max数组中使用二分法即可求出第一个大于等于k的位置,即可按照上述思路求解
3.给定一个无序矩阵,其中只有1,0两种值,求只含有1的最大子矩阵大小,矩阵大小用其中的元素个数来表示
时间复杂度O(m*n)
依次遍历必须以每一行为底的矩阵的最大子矩阵大小
a[5][5]={
{0,1,1,0,1},
{1,1,0,1,1},
{1,0,0,1,1},
{0,1,1,0,0},
{1,1,0,1,1}
}
s[0] = {0,1,1,0,1}
s[1] = {1,2,0,1,2}
s[2] = {2,0,0,2,3}
s[3] = {0,1,1,0,0}
s[4] = {1,2,0,1,1}
即形成一个直方图,依次求最大的矩形
假设一个直方图为{3,4,5,4,3,2}
建立一个大顶栈,首先3进栈,接下来进栈4,由于4>3,则4直接进栈,同理5也进栈,由于4<=5,则开始计算当前栈顶的5,因为当前的4是5的右边的第一个小于等于5的数,即右边界为4,栈顶的下面的数,一定是第一个左边的比栈顶小于等于的数,故栈顶5下面的4,是左边界,5出栈
在实际情况下,该栈中压入的是index
思路:语文表达能力有限,下面举例说明
假设一个矩阵a[4][5]={
{3,4,2,1,-5},
{2,2,5,-6,4},
{-5,2,1,3,3},
{-3,4,-2,-2,3}
}
建立一个辅助数组sum[m*(m+1)/2][n],分别记录遍历行组合的每一列的和:
sum[1][i] = {3,4,2,1,-5}
//若子矩阵被包含于第一行,则其各列累加和为sum[1],此时只需求累加和最大的子数组即可,故子矩阵最大累加和为3+4+2+1=10
sum[2][i] = {5,6,7,-5,-1{}
//若子矩阵被包含于第一行和第二行,则其各列累加和为sum[2],此时子矩阵最大累加和为5+6+7=18
sum[3][i] = {0,8,8,-2,2}
//若子矩阵被包含于第一行、第二行和第三行,则其各列累加和为sum[3],此时子矩阵最大累加和为0+8+8+(-2)+2=16
依次类推,sum[4]表示子矩阵被包含于前四行,sum[5]表示只被包于含第二行,sum[6]表示只被包含于第二行和第三行,等等
即:1,12,123,1234,12345,2,23,234,2345,3,34,345,4,45,5
之后即可在sum中找出累加和最大的子矩阵之和
时间复杂度为O(m²*n),即若m<n时,记录列累加和,若m>n,则辅助数组记录行累加和
也可以依据要求减小辅助数组的空间
public static int MaxSum(int[][] m){
if(m == null || m.length == 0|| m[0].length == 0){
return 0;
}
int max = Integer MIN_VALUE;
int cur = 0;
int[] s = null;
for(int i = 0;i !=m.length;i++){
s = new int[m[0].length];
for(int j = i; j!=m.length;j++){
cur = 0;
for(int k = 0; k!= s.length;k++){
s[k] += m[j][k];
cur += s[k];
max = Math.max(max, cur);
cur = cur < 0 ? 0 : cur;
}
}
}
return max;
}
2.给定一个无序矩阵,给定一个值k,求累加和小于等于k的最大子矩阵大小,矩阵大小用其中的元素个数来表示
思路:
假设有一个数组,给定一个值k,求累加和小于等于k的最长子数组长度,假设必须以j位置为子数组的结尾位置,从开始位置累加至j位置为sum[j],并且sum[j]>=k,则必定有一个位置i<j,使得sum[i]>=sum[j]-k,(注,此时i位置为最早的sum[i]>=k的位置)则必须以j位置结尾的子数组且和小于等于k的最长长度为j-i。遍历数组即可求解。
举例:
a[] = {3,-1,2,-1,4,-2,5}
sum[] = {3,2,4,3,7,5,9}//累加和数组
max[] = {3,3,4,4,7,7,9}//累加和的区域最大值数组
在max数组中使用二分法即可求出第一个大于等于k的位置,即可按照上述思路求解
public static int max SubMatrixSumLessThank(int[][] m, int sum){
//在数组中求小于等于sum值的最长的长度
if(m == null || m.length == 0 || m[0] == null || m[0].length == 0){
return 0;
}
int res = 0;
for(int i = 0; i<m.length;i++){
int[] sumArr = new int[m[0].length];
for(int j = i; j<m.length;j++){
for(int k = 0;k <m[0].length;k++){
sumArr[k] += m[j][k];
}
res = Math.max(res, (j-i+1)*maxLength(sumArr, sum));
}
}
return res;
}
public static int maxLength(int[] arr, int k){
int[] h = new int(arr.length + 1);
int sum = 0;
h[0] = sum;
for(int i = 0; i!= arr.length;i++){
//累加的递增数组
sum+=arr[i];
h[j+1] = Math.max(sum,h[i]);
}
sum = 0;
int res = 0;
int pre = 0;
int len = 0;
for(int i = 0;i != arr.length;i++){
//以每个位置结尾的最长长度
sum += arr[i];
pre = getLessIndex(h, sum-k);
len = pre == -1 ? 0 : i - pre + 1;
res = Math.max(les, len);
}
return res;
}
public static int getLessIndex(int[] arr, int num){
int low = 0;
int high = arr.length -1;
int mid = 0;
int res = -1;
while(low <= high){
mid = (low + high)/2;
if(arr[mid] >= num){
res = mid;
high = mid -1;
}else{
low = mid +1;
}
}
return res;
}
3.给定一个无序矩阵,其中只有1,0两种值,求只含有1的最大子矩阵大小,矩阵大小用其中的元素个数来表示
时间复杂度O(m*n)
依次遍历必须以每一行为底的矩阵的最大子矩阵大小
a[5][5]={
{0,1,1,0,1},
{1,1,0,1,1},
{1,0,0,1,1},
{0,1,1,0,0},
{1,1,0,1,1}
}
s[0] = {0,1,1,0,1}
s[1] = {1,2,0,1,2}
s[2] = {2,0,0,2,3}
s[3] = {0,1,1,0,0}
s[4] = {1,2,0,1,1}
即形成一个直方图,依次求最大的矩形
假设一个直方图为{3,4,5,4,3,2}
建立一个大顶栈,首先3进栈,接下来进栈4,由于4>3,则4直接进栈,同理5也进栈,由于4<=5,则开始计算当前栈顶的5,因为当前的4是5的右边的第一个小于等于5的数,即右边界为4,栈顶的下面的数,一定是第一个左边的比栈顶小于等于的数,故栈顶5下面的4,是左边界,5出栈
在实际情况下,该栈中压入的是index
public static int maxRecSize(int[][] map){
if(map == null || map.length == 0 || map[0].length == 0){
return 0;
}
int maxArea = 0;
int[] height = new int[map[0].length];
for(int i = 0; i<map.length; i++){
for(int j = 0; j < map[0].length; j++){
height[j] = map[i][j] == 0 ? 0: height[j] + 1;
}
maxArea = Math.max(maxArea, maxRecFromBotton(height));
}
return maxArea;
}
public static int maxRecFromBottom(int[] height){
if(height == null || height.length == 0){
return 0;
}
int maxArea = 0;
Stack<Integer> stack = new Stack<Integer>();
for(int i = 0; i < height.length; i++){
while(!stack.isEmpty() && height[i] <= height[stack.peek()]){
int j = stack.pop();
int k = stack.isEmpty() ? -1: stack.peek();
int curArea = (i - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
stack.push(i);
}
while(!stack.isEmpty()){
int j = stack.pop();
int k = stack.isEmpty() ? -1: stack.peek();
int curArea = (height.length - k -1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
return maxArea;
}