详解如何求解最大子矩阵

前言

        最近在学dp(dynamic programming:动态规划)的时候遇到了一道最大子矩阵的问题,当时即使学了最大序列和,但看到这个问题时还是无从下手,不知道如何确定最大子矩阵,然后去看别人的代码,还是有点看不懂别人在写啥,所以这篇博客记录自己对这个问题理解~

问题在这

分析

这里就不介绍最大序列和了,如果没了解过的话需要补充一下这方面的知识,点此跳转,但他在讲最大子矩阵的时候,并没有过多阐述如何得到最优子矩阵,而是告诉我们如何计算最优子矩阵的值,然后给出代码,导致看的懵懵懂懂。所以也就为啥有了这篇博客的原因了~

事实上,计算最大子矩阵是把二维数组,压缩成一维数组,来计算该一维数组的最大序列和。当然这么说,很多人都是很迷糊的,具体往下看。

首先,我们如何计算一个矩阵大小(定义为矩阵中所有元素的和。)

最简单的方法就是先把每行的元素都相加,最后结果变成一列,然后把这一列元素相加,最终得到矩阵的大小(如图1左图所示)。或者是先把每一列的元素都相加,最后结果变成一行,然后这一行元素相加,最终得到矩阵的大小(如图1右图所示),这就是上面说的2维压缩成1维的思想。

为了方便理解使用最长序列和,我们采用图1右图的方式计算最大子矩阵。

 图1

那么问题来了,如何找到最大的那个子矩阵呢?最简单的方法是,一个矩阵中能有多少个满列子矩阵,那么我们就枚举所有的满列子矩阵。找满列子矩阵的原因是因为我们计算最大子矩阵的时候,是采用2维压缩成1维的方式计算的,于是对于压缩后的每一行,采用最长序列和计算该行中哪几个连续的列组成的矩阵最大,并记录子矩阵中最大的那一个就好了。

文字看不懂得话看下面的图.ps:下图的abcd指的是a行,b行,c行,d行。

图2

于是我们得到了10个(1+2+3+4)满列子矩阵,图中展示的是满列子矩阵压缩成1维的样子。换句话来说,这样我们就得到了10个一维序列,于是我们只需要用求最长序列和的方式,计算这10个一维序列中,哪个子序列和最大,并记录就好了,其结果也就是我们所要求的最大子矩阵了。

我们站在已知图2中所求矩阵的结果的角度往回看,图中的最大子矩阵为\begin{bmatrix} 9 &2 \\ -4&1 \\ -1&8 \end{bmatrix},压缩成1维的话对应图2中的(d+c+b)所表示的满列子矩阵,由于该序列\begin{bmatrix} 4& 11 & -10 & 1 \end{bmatrix}的最大序列和是15也就是前两个数相加,从而该值就是最大子矩阵的大小。

至此,我们已经在理论上知道如何求一个矩阵的最大子矩阵大小了,现在我们看看代码如何书写

代码

#include <iostream>
#include <limits>
#include <vector>
#include <numeric>
using namespace std;

// 查找一维序列的最长子序列和
// 如果不知道如何求最长子序列和,建议看看上文中的链接,这里不过多赘述
int get_max_sequence_sum(vector<int> arr){
    for(int i=1;i<arr.size();++i){//dp思想
        arr[i]=max(arr[i-1]+arr[i],arr[i]);
    }
    int ans=arr[0];//寻找最长子序列和的结果
    for(auto x:arr){
        if(x>ans)ans=x;
    }
    return ans;
}
int main() {   
    int n;
    cin>>n;
    vector<vector<int>> matrix(n,vector<int>(n,0));//初始化矩阵
    int v;
    for(int i=0;i<n;++i){
        for (int j=0; j<n; ++j) {
          cin>>v;
          matrix[i][j]=v;//初始化矩阵的大小  
        }
        cin.ignore();
    }
    int ans=numeric_limits<int>::min();//ans记录最大子矩阵的大小,初始为最小值
    for(int i=0;i<n;++i){//这一层循环控制i行的加入,也就是图2三条虚线的四个过程
        vector<int> col_sum(n,0);//满列子矩阵的一维表示
        for(int j=i;j>=0;--j){//这一层循环控制当前所在的行共有多少个满列子矩阵
            /**
             *计算满列矩阵,具体计算过程如图2所示
             * 如j=i=c行,那么计算的过程是先算c行,在算c+b,最后在算c+b+a
             */
            for(int k=0;k<n;++k){
                col_sum[k]+=matrix[j][k];
            }
            // 每得到一个一维序列,也就是图2的10个,就计算该序列的最大子序列和
            int pos=get_max_sequence_sum(col_sum);
            if(pos>ans)ans=pos;//记录最大值
        }
    }
    cout<<ans<<endl;
}

这就是最大子矩阵的计算方式啦,大家看到这可以去尝试手动ac上面那道最大子矩阵的题啦~

  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值