有关子矩阵最大累加和的总结

来源:互联网 发布:淘宝装修模板 编辑:程序博客网 时间:2024/06/05 05:57
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,则辅助数组记录行累加和

也可以依据要求减小辅助数组的空间

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;}