二维前缀和的扩展

最大子矩阵

https://www.nowcoder.com/practice/a5a0b05f0505406ca837a3a76a5419b3

如果说,这题用一个预处理一个二维前缀和,然后枚举子矩阵的左上角与右上角,那么我们可以用O(n4)O(n^4)的时间复杂度得到答案。不过对于这题n=100来说,1004=108100^4=10^8会超时。

所以我们要想办法优化,最蛋疼的点其实在于暴力的枚举会涉及非常多无用的状态。我们可以优化枚举,转而变成枚举上界与下界。

alt

类似这样,两条红线是我们枚举的上下界。那么问题来了,如何快速的得到两条红线夹出来的子矩阵里的最大子矩阵?(我称之为子子矩阵)

我们可以考虑这个问题的简化版。如果快速得到一个序列里的最大子序列和?这是一个经典的线性dp问题,可以用O(n)O(n)时间复杂度解决。我们这简化问题迁移到原问题上面来,这样一个子矩阵里的最大子子矩阵,要套用简化问题里的dp方程,我们必须得首先把整个列压缩成一个元素。(把一列看成一个元素,那么整个矩形就被压缩成了一个序列)而这启发我们在列上做一个前缀和。

只要在列上做了一个前缀和,那么我们就可以直接套用最大子序和的dp方程来O(n)O(n)得到最大子子矩阵。而枚举子矩阵是用两条红线来枚举的,时间复杂度是O(n2)O(n^2)。因此总的时间复杂度就是O(n3)O(n^3)

#include <iostream>

using namespace std;

const int N = 110;

int n;
int grid[N][N];
int s[N][N];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            scanf("%d", &grid[i][j]);
    
    //做一个列方向上的前缀和
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            s[i][j] = s[i - 1][j] + grid[i][j];
    
    int res = -1e9;
    for (int i = 1; i <= n; ++i)
        for (int j = i; j <= n; ++j)
        {
            int f = 0; //进行最大子序列和的dp
            for (int k = 1; k <= n; ++k)
            {
                int w = s[j][k] - s[i - 1][k];
                f = max(0, f) + w;
                res = max(res, f);
            }
        }
    printf("%d\n", res);
    return 0;
}
全部评论

相关推荐

2本硕,在这一个下午真的绷不住了,浪费了太多时间,现在的技术栈还停在C语言和stm32上,找嵌入式的实习面试被拷打,找杭州的一个也找不到,真的心里难受,linux没学过,研二了开始慌了。
一条淡水魚:嵌入式这行的面试我认为实际项目比较重要,技术栈简单的提一嘴就行,面试官在乎的关键点在于你用了这些技术做了哪些工作解决了什么问题,而不是停留在离散的那些个技术栈上,那除了教课没有意义,好比你提到的c语言和32,你用32做过哪些具体的项目?接触过什么外设?使用过哪些公司的SDK?有没有实际产品落地?以及各种只有进入真正的生产环节当中才会积累到的经验......主动去和面试官讨论这些实际的问题,甚至还能就某个具体参数的合理性与他去简单探讨一下,只要技术栈对口,基本上就稳啦~(另外linux和RTOS是嵌入式的标配哦,选一个方向走下去吧)
点赞 评论 收藏
分享
评论
13
3
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务