动态规划---例题4.最大子矩阵和问题

本题与力扣面试题 17.24. 最大子矩阵相同.

一.问题描述

给定一个正整数、负整数和 0 组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。
返回一个数组 [r1, c1, r2, c2],其中 r1, c1 分别代表子矩阵左上角的行号和列号,r2, c2 分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。

二.解题思路

如果有看过经典例题3 — 最大子段和,就会很快知道该问题的突破口.
我们知道,最大子段和是针对一维数组而言,可以找到该数组中连续子数组之和最大的那一个.而对于本题,我们需要求解最大子矩阵和,就是将一维问题转化为了二维问题.最大子矩阵和问题是最大子段和问题向二维的推广.

我们用a[1:m] [1:n]表示给定的m行n列的整数矩阵.子数组a[i1:i2] [j1:j2]表示左上角和右下角行列坐标分别为(r1, c1)和(r2, c2)的子矩阵,
其各元素之和记为:
在这里插入图片描述
在这里插入图片描述

容易看出(不太容易😂):

我们只要记住,将二维数组压缩成为一维数组即可.
这正是一维情形的最大子段和问题.由此,借助最大子段和问题的动态算法MaxSum,可设计出解最大子矩阵和问题的动态规划算法getMaxMatrix如下:
通过MaxSum这个函数得到一维数组中最大连续子数组的起始左边界和右边界.

// 最大子矩阵和
#include<bits/stdc++.h>
using namespace std;
// 正确,挺傻逼的我
int MaxSum(vector<int> &nums, int &left, int &right)   //找到最大子序列和,并返回起始位置left和结束位置right
{
    int n = nums.size();
    if(n==0) return {};
    int begin = 0;
    int pre = INT_MIN, maxAns = nums[0];
    for(int i=0; i<n; ++i)
    {
        if(pre > 0)
        {
            pre = pre + nums[i];
        }
        else 
        {
            begin = i;
            pre = nums[i];
        }
        if(pre > maxAns)  //更新
        {
            maxAns = pre;
            left = begin;  //记住只有满足pre>maxAns这个条件之后才能够更新left,right.不然就可能会出现left > right的现象.40/45惨痛教训
            right = i;
        }
    }
    return maxAns;
}
vector<int> MaxSubMatrix(vector<vector<int>> &matrix)
{
    int m = matrix.size();
    int n = matrix[0].size();
    vector<int> b(n);
    vector<int> res(4);
    int ans = INT_MIN;
    for(int i=0; i<m; ++i)
    {
        for(int k=0; k<n; k++) b[k] = 0;    //重置b数组
        for(int j=i; j<m; ++j)
        {
            for(int k=0; k<n; ++k)  
            {
                b[k] += matrix[j][k];  
                //我们只是不断增加其高,也就是下移矩阵下边,所有这个矩阵每列的和只需要加上新加的那一行的元素
                //因为我们求dp[i]的时候只需要dp[i-1]和nums[i],所有在我们不断更新b数组时就可以求出当前位置的dp_i
            }
            int left = 0, right = 0;
            int max = MaxSum(b, left, right);

            // cout<<"left:"<<left<<"  right:"<<right<<"  max:"<<max<<endl;
            // cout<<"i:"<<i<<" j:"<<j<<endl;

            if(max > ans)
            {
                ans = max;
                res[0] = i;
                res[1] = left;
                res[2] = j;
                res[3] = right;
            }
        }
    }
    cout<<"最大子矩阵和为:"<<ans<<endl;
    return res;
}
int main()
{
    int m, n; 
    vector<int> res;
    cout<<"请输入矩阵的行数和列数:";
    while(cin>>m>>n && m && n)
    {
        vector<vector<int>> matrix(m, vector<int>(n, 0));
        cout<<"请输入矩阵元素:"<<endl;
        int num;
        for(int i=0; i<m; ++i)
        {
            for(int j=0; j<n; ++j)
            {
                cin>>num;
                matrix[i][j] = num;
            }
        }
        res = MaxSubMatrix(matrix);
        // res = getMaxMatrix(matrix);
        cout<<"最大子矩阵的左上坐标:("<<res[0]<<","<<res[1]<<"), 右下坐标:("<<res[2]<<","<<res[3]<<")"<<endl;
        cout<<"请输入矩阵的行数和列数:";
    }
    system("pause");
    return 0;
}

运行结果:
在这里插入图片描述

本篇文章参考我的老师毕方明《算法设计与分析》课件.
欢迎大家访问我的个人博客 — 乔治的编程小屋,和我一起为大厂offer努力吧!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值