最大的和(最大子矩阵和:贪心&&前缀和+dp)

给定一个包含整数的二维矩阵,子矩形是位于整个阵列内的任何大小为1 * 1或更大的连续子阵列。

矩形的总和是该矩形中所有元素的总和。

在这个问题中,具有最大和的子矩形被称为最大子矩形。

例如,下列数组:

0 -2 -7 0 
9 2 -6 2 
-4 1 -4 1 
-1 8 0 -2 

其最大子矩形为:

9 2 
-4 1 
-1 8 

它拥有最大和15。

输入格式

输入中将包含一个N*N的整数数组。

第一行只输入一个整数N,表示方形二维数组的大小。

从第二行开始,输入由空格和换行符隔开的N2N2个整数,它们即为二维数组中的N2N2个元素,输入顺序从二维数组的第一行开始向下逐行输入,同一行数据从左向右逐个输入。

数组中的数字会保持在[-127,127]的范围内。

输出格式

输出一个整数,代表最大子矩形的总和。

数据范围

1≤N≤100

输入样例:

4
0 -2 -7 0 9 2 -6 2
-4 1 -4  1 -1

8  0 -2

输出样例:

15

思路:因为要求矩阵所有数的和,所以可以首先处理出纵向的每一竖列的前缀和,将求一个竖列的和的时间复杂度从o(n)优化成o(1),只需要算出a[j][k]-a[i-1][k],就是以第i行为上界,以第j行为下界,第k列这一竖列的和(虽然只有一条竖线,但也可以看成一个矩形,一个矩形就是由这若干条竖线组成的),然后利用贪心的思想,类似dp的处理方法,在矩阵的上下界(i和j确定的矩阵的高)固定的情况下,枚举k从1到n(即矩阵的宽),要求最大矩阵和,每次一个竖列向右扩展时,得到的矩阵的最大和都与上一个状态的最大和有关,这就涉及到决策的最优问题(类似dp最大不相交字段和),就把二维的问题转化成了一维的问题,这里只不过是之前在求dp最大不相交字段和问题中的每一个数都变成了一条线段的和(这条线段的和使用上面前缀和的公式o(1)次的算出的),所以每次决策时,当前最大和=max(上一次的最大和,0)+当前竖列的和(即:上一个最大和>0就累加上,否则当前竖列的和就是当前最大和)

最后总结一下,就是枚举上下界(高度)(o(n^2)),然后枚举所有宽度(o(n)),用dp+前缀和算出当前最大和,这样时间复杂度就是o(n^3).

完整代码:

#include <iostream>

using namespace std;
const int inf=0x3f3f3f3f;
int n,a[110][110];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
            a[i][j]+=a[i-1][j];//a[i][j]表示第j列以1为上界i为下界所构成的这一竖列的和
        }
    }
    int ans=-inf;
    for(int i=1;i<=n;i++){//枚举上界
        for(int j=i;j<=n;j++){//枚举下界
            int sum=0;
            for(int k=1;k<=n;k++){//枚举所有宽度
                sum=max(sum,0)+a[j][k]-a[i-1][k];
                ans=max(ans,sum);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}
发布了176 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Mr_Kingk/article/details/104445941