1求整形矩阵的最大和子矩阵

具体问题

给你一个N×N的数组,请你找出有最大和的子区域,其和为多少。一个区域的和指的是该区域中所有元素值的和。一个区域是指相连的任意大小的子数组。例如,对以下的二维数组,其最大和的子区域位于左下角,其和为15,如下所示。

假设所求N*N的矩阵的最大子矩阵是从i列到j列,q行到p行,如下图所示(假设下标从1开始)

  a[1][1]  a[1][2]  ······  a[1][i]  ······  a[1][j]   ······  a[1][n]

  a[2][1]  a[2][2]  ······  a[2][i]  ······  a[2][j]   ······  a[2][n]

                  ······

  a[q][1]  a[q][2]  ······  a[q][i]  ······  a[q][j]  ······  a[q][n]

                  ······

  a[p][1]  a[p][2]  ······  a[p][i]  ······  a[p][j]  ······  a[p][n]

                  ······

  a[n][1]  a[n][2]  ······  a[n][i]  ······  a[n][j]  ······  a[n][n]

提示:最大子矩阵就是图示红色部分,如果把最大子矩阵同列的加起来,我们可以得到一个一维数组{a[q][i]+······+a[p][i] , ······ ,a[q][j]+······+a[p][j]} ,现在我们可以看出,这其实就是一个一维数组的最大子段问题。如果把二维数组看成是纵向的一维数组和横向的一维数组,那问题就迎刃而解了,把二维最大子矩阵问题转换成了我们已解决的一维最大子段和问题。


1.错误案例(自己编写)

此代码是在没理解同列的加起来的到一个的时候自己编写的代码,只是以为提示是一个如何计算最大子矩阵的方法的理解,因为刚刚学完一维最大子段和问题,所以我认为只需要比较

  • 行最大字串
  • 列最大字串
  • 行和列最大字串构成的子矩阵

求其最大值然后就求出此子矩阵即可。结果并非如此

代码如下(可先看结论然后看代码,此过程也可忽略因为是本人的错误实例,但也能实现,做个反例吧)

import java.util.ArrayList;
import java.util.Collection;public class Ex2 {static int LEFT = 0;static int RIGHT = 0;public static void main(String[] args) {int[][] intArray = getArray();int row = 0;   // 记录其最大行字串值行数,后续需要在其行中查找Double max = Double.MIN_VALUE;for(int i = 0; i<intArray.length; i++) {  // 返回整型最大字串值double temp = getMaxSubString(intArray[i]);if(temp > max) {max = temp;row = i;}}Collection<Integer> coll = new ArrayList<>();coll = getMaxSubstrRange(max, intArray[row]);int [] intRange = coll.stream().mapToInt(Integer :: valueOf).toArray();  /* 想要转换成int[]类型,就得先转成IntStream。这里就通过mapToInt()把Stream调用Integer::valueOf来转成IntStream而IntStream中默认toArray()转成int[]。   */int column = intRange[0];  // 取出左边界值,由所取最大行字串和最大列子串的位置决定int[][] intArray2 = transposiotion(intArray);int row2 = 0;   //记录所在最大行字串值行数Double max2 = Double.MIN_VALUE;for(int i = 0; i < intArray2.length; i++) {double temp =getMaxSubString(intArray2[i]);if(temp > max2) {max2 = temp;row2 = i;}}Collection<Integer> coll2 = new ArrayList<>();coll2 = getMaxSubstrRange(max2, intArray2[row2]);int [] intRange2 = coll2.stream().mapToInt(Integer :: valueOf).toArray();  //同上    int column2 = intRange2[1];  // 取出右边界值int temp = row2;   //因为转置所以行和列号需要交换row2 = column2;column2 = temp;double total = 0;   // 记录由最大行字串和最大列矩阵构成的子矩阵for(int i = Integer.min(row, row2); i <= Integer.max(row, row2); i++) {for(int j = Integer.min(column, column2); j <= Integer.max(column, column2); j++) {total += intArray[i][j];}}System.out.println("最大子区域为"+Double.max(Double.max(max, max2), total));}public static int[][] getArray() {int[][] intArray = {{0, -2, -7, 0}, {9, 2, -6, 2}, {-4, 1, -4, 1 }, {-1, 8, 0, -2}};return intArray;}// 获得一维数组的最大子串值public static double getMaxSubString(int[] array) {double maxSubString = Double.MIN_VALUE;         //储存最大字串值for(int i = 1; i <= array.length; i++) {        //字串大小for(int lo = 0, hi = lo+i-1 ; lo < array.length - i + 1; hi++, lo++) {  //确定子串的具体情形,即为对所有符合该长度的子串遍历int total = 0;                      // 储存累加值,每次区间变化则对其重新赋值0for(int j = lo; j <= hi ; j++) {                                   // 对其具体的区间进行累加total += array[j];}if(total > maxSubString) {maxSubString = total;}}}return maxSubString;}// 返回左右边界值,以集合形式返回public static Collection<Integer> getMaxSubstrRange(double maxSubString, int[] array) {int left = 0;int right = 0;double maxSubString1 = getMaxSubString(array);if(maxSubString1 != maxSubString) {        // 如果最大字串不匹配return null;}else {// 最大字串匹配for(int i = 1; i <= array.length; i++) { //  字串长度for(int lo = 0, hi = lo+i-1 ; lo < array.length - i + 1; hi++, lo++) {  //确定区间的具体情形int total = 0;                       // 储存累加值,每次区间变化则对其重新赋值0for(int j = lo; j <= hi ; j++) {                                   // 对其具体的区间进行累加total += array[j];}if(total == maxSubString1) {left = lo;            // 记录最大字串起始索引right = hi;           //记录最大字串终止索引}}}}Collection<Integer> coll = new ArrayList<>();coll.add(left);    // 返回左边区间值coll.add(right);     // 返回右边区间值return coll;}public static int[][] transposiotion(int[][] array){int[][] newArray = new int[array[0].length][array.length];  //转置之后的数组for(int i = 0; i < array.length; i++) {for(int j = 0; j < array[0].length; j++) {newArray[i][j] = array[j][i];}}return newArray;}
}// 运行结果:最大子区域为15.0

结论

下面编写有很大的一个局限性,虽然能成功的运行结果。

局限1:当有求行最大字串和列最大字串时,只能取循环将要结束时候的最大字串,可能会对之前的相同值忽略。此时可能的到最大子矩阵的结果并不是实际最大值。

局限二:当确定求出行最大行子矩阵和和最大列矩阵时由于他们的位置关系,确定子矩阵的上下左右边界时候结果也会不同。理解图如下

备注:此时只需知道最大行矩阵子串(9,2)中9的行列和最大列矩阵(2,1,8)中8的最大行列就可以确定其子矩阵的上下左右的边界。

2.正确方式求解此问题(借鉴辨认代码+理解)代码原创https://blog.csdn.net/carson0408/article/details/78712510

本文主要讲的是关于矩阵的子方阵问题,典型题型有:所有元素为1的最大子方阵、最大和子矩阵。

首先讲述的是所有元素为1的最大子方阵实现算法:先引入一个res[][]数组,res[i][j]计算以以B[i][j]在内的子方阵的大小,此时B[i][j]在子方阵的右下角。因此初始是先对第一行和第一列res数组直接由原数组B的第一行和第二列赋予即可。然后对其它行和列赋值,若B[i][j]=0,则对应的res[i][j]=0,若为res[i][j]=1则取三者中res[i-1][j]、res[i-1][j-1]、res[i][j-1]中最小值再加1所得的值。

代码如下

​
import java.util.Scanner;public class MatrxWithAllOnes {public static void main(String[] args) {Scanner sc = new Scanner(System.in);System.out.println("输入矩阵的大小");String[] str = sc.nextLine().split(" ");int m = Integer.parseInt(str[0]);int n = Integer.parseInt(str[1]);int[][] B = new int[m][n];int k = 0;System.out.println("输入原矩阵");while(k<m && sc.hasNextLine()) {String[] str1 = sc.nextLine().split(" ");for(int i = 0; i<str1.length; i++) {B[k][i] = Integer.parseInt(str1[i]);}k++;}System.out.println("所有的元素为1的最大子方阵");matrixWithAllOnes(B, m ,n);}public static void matrixWithAllOnes(int[][] B, int m, int n) {int[][] res = new int[m][n];int max_of_s, max_i, max_j;// 对第一列进行赋值for(int i = 0; i < m; i++) {res[i][0] = B[i][0];}// 对第一行赋值for(int i = 0; i < n; i++) {res[0][i] = B[0][i];}// 对除此之外的其他数组位置进行赋值for(int i = 1; i < m; i++) {for(int j = 1; j < n; j++) {if(B[i][j] == 1)res[i][j] = Math.min(Math.min(res[i][j-1], res[i-1][j-1]), res[i-1][j]) + 1;else   //B[i][j] = 0res[i][j] = 0;}}max_of_s = res[0][0];max_i = 0;  // res中最大值的行max_j = 0;  // res中嘴的值的列//遍历找到最大值for(int i = 0; i < m; i++) {for(int j = 0; j < n; j++) {if(res[i][j] > max_of_s) {max_of_s = res[i][j];max_i = i;max_j = j;}}}// 遍历子方阵System.out.println("Maximux sub-matrix");for(int i = max_i - max_of_s + 1;  i <= max_i;  i++) {  // 循环从子方阵左上交元素行索引for(int j = max_j - max_of_s + 1;  j <= max_j  ;  j++) // 循环从子方阵左上角元素列索引System.out.print(B[i][j]+" ");System.out.println();}System.out.println("最大的子方阵的大小:");System.out.println(max_of_s +"x"+ max_of_s);}}​

测试结果

输入矩阵的大小
6 5
输入原矩阵
1 1 1 1 1
0 1 0 1 1
0 1 0 1 0
0 0 0 0 1
1 1 1 1 0
0 1 0 1 0
所有的元素为1的最大子方阵
Maximux sub-matrix
1 1
1 1
最大的子方阵的大小:
2x2

然后需要讲述的问题是求最大子矩阵问题。此问题的算法为:先建立一个与A相同的数组M[m][m],此数组M[i][j]的值等于该原数组第j列A[0][j]+A[1][j]+...+A[n][j];此时对res赋值时。所以对第一行进行赋值只需将原数组A的第一行值分别赋值到M数组第一行即可。当对M其他位置进行赋时则M[i][j]=M[i-1][j]+A[i][j],该数组则已经完成赋值。当计算最大和子矩阵,只需要对其M数组进行处理。提示:若要计算图片的最大和字串问题时只需j将M[3][k]-M[0][k]的值取k = 0 ,1的值相加即可得出15.

代码如下:

package chapter4.组525邱勇418;public class FindMaxSumSubMatrix {public static void main(String[] args) {int[][] A = {{2, -2, -7, 0}, {9, 2, -6, 2}, {-4, 1, -4, 1}, {-1, 8, 0 ,-2}};int n = A.length;findMaxSumSubMatrix(A, n);}// 找出最大字串矩阵的总和public static void findMaxSumSubMatrix(int[][] array, int n) {int[][] M = new int[n][n];// 对特殊矩阵进行赋值for(int i = 0; i < n; i++) {for(int j = 0; j < n; j++) {if(i == 0) {M[i][j] = array[i][j];} else {M[i][j] = array[i][j] + M[i-1][j];}}}int maxSoFar = 0;     //存最大值int min, subMatrix;for(int i = 0; i < n; i++) {  // 对矩阵进行分割的上边界for(int j = i; j < n; j++) {   //对矩阵进行分割的下边界,下边界不能比上边界小,最坏也是二者相等min = 0;        //每次对下边界重新取值是需要重新定义min(最小子串和矩阵)的值和subMatrix(存储子串和矩阵)的值,subMatrix = 0;for(int k = 0; k < n; k++) {  //向右边延申的长度if(i == 0) subMatrix += M[j][k];     // 上边界为0else {subMatrix += M[j][k] - M[i-1][k];   // j=1,i=1,表示只取第二行数据}if(subMatrix < min)        //记录其左边的值,只取最小值min = subMatrix;if((subMatrix - min) > maxSoFar)maxSoFar = subMatrix - min;        // 去除左边部分子矩阵的最小值,只取右边子矩阵的值}}}System.out.println(maxSoFar);}}

运行结果

15

求整形矩阵的最大子矩阵相关推荐

  1. 求一个矩阵的最大子矩阵

    #include <iostream> #include <string> #include <assert.h> #include <malloc.h> ...

  2. 求一个3*3的整形矩阵对角线元素之和

    /*求一个3*3的整形矩阵对角线元素之和*/#include <stdio.h>int main() {int a[3][3],i,j,sum = 0;printf("请输入整数 ...

  3. [ZJOI2007]棋盘制作 悬线法dp 求限制下的最大子矩阵

    https://www.luogu.org/problemnew/show/P1169 第一次听说到这种dp的名称叫做悬线法,听起来好厉害 题意是求一个矩阵内的最大01交错子矩阵,开始想的是dp[20 ...

  4. 【100题】三十五 求一个矩阵中最大的二维矩阵(元素和最大)

    一,题目: 求一个矩阵中最大的二维矩阵(元素和最大).如: 1 2 0 3 4 2 3 4 5 1 1 1 5 3 0 中最大的是: 4 5 5 3 要求:(1)写出算法;(2)分析时间复杂度;(3) ...

  5. 使用SVD求取矩阵的伪逆

    ➤01 矩阵的SVD分解 对于矩阵A∈Rn×mA \in R_{n \times m}A∈Rn×m​,可以通过奇异值分解(Singular Vector Decomposite)分解成如下形式:A=U ...

  6. python求向量函数的雅可比矩阵_在python Numpy中求向量和矩阵的范数实例

    np.linalg.norm(求范数):linalg=linear(线性)+algebra(代数),norm则表示范数. 函数参数 x_norm=np.linalg.norm(x, ord=None, ...

  7. python二维元素向量_详解python Numpy中求向量和矩阵的范数

    在python Numpy中求向量和矩阵的范数实例 np.linalg.norm(求范数):linalg=linear(线性)+algebra(代数),norm则表示范数. 函数参数 x_norm=n ...

  8. [振动力学] 使用能量法求质量矩阵的时候需要注意刚体运动分解

    使用能量法求质量矩阵的时候,对于杆的动能,我有两种写法 ①求出的质量矩阵没有耦合项,而②有耦合项 显然正确答案只有一个,那就是② 在①中我是把运动分解的基点取在了杆和圆柱体的连接点处,实际上并不可以把 ...

  9. C语言每日一练——第10天:求一个矩阵主对角线及副对角线元素之和

    C语言每日一练 2021年9月18日 题目描述 求一个矩阵主对角线及副对角线元素之和 分析 在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合,矩阵是高等代数学中的常见工具,也常见于 ...

最新文章

  1. 【CV冬季划】终极进阶,超30个项目实战+3本书+3年知识星球
  2. javascript 嵌入python_从Javascript代码调用Python函数
  3. QT使用中的一些记录
  4. Java旅游动吧项目讲解_springboot动吧项目
  5. ajax 服务器怎么响应,Ajax 接收服务器返回的json响应方法
  6. 【Java编程思想笔记】-集合1
  7. androidstudio打包apk 文件_Android 缩小apk体积过程记录
  8. centos 切换用户_【站长亲测】迈拓维矩kvm切换器8口usb高清VGA显示器鼠标键盘切屏共享器8进1出...
  9. 人人开源代码生成器启报错
  10. 10的n次方 java_为大家一共一个10的n次方的算法
  11. 【MySQL】011-多表查询
  12. java中的异或交换位置_java异或实现两个变量交换
  13. 用matlab算特征值,用Matlab用计算特征值和特征向量
  14. redis管理_Redis 桌面管理工具Redis Desktop Manager
  15. Andriod 系统移植基础
  16. Leetcode-二分+递归/回溯-1723. 完成所有工作的最短时间
  17. git+vimdiff 一次关闭所有文件
  18. Android高级工程师进阶学习,分享PDF高清版
  19. ios SDWebImage 加载图片流程
  20. 支付宝个人收款解决方案

热门文章

  1. Windows关闭程序托盘图标不能消失的原因
  2. Java中方法的注意事项
  3. electron 总结笔记
  4. 46道史上最全Redis面试题,面试官能问的都被我找到了(含答案)
  5. matlab 画海面图,大海怎么画?波光粼粼的的海面画法是什么?
  6. Cglib动态代理-MethodInterceptor的简单使用(转载)
  7. SDNU 1206.蚂蚁感冒 【代码如此简单,思维练习】【7月29】
  8. Element常用组件—表格、表单、对话框和分页工具条
  9. 微前端架构:如何由内而外取代单体架构
  10. XGBOOST算法过程