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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值