zoukankan      html  css  js  c++  java
  • 最大子矩阵问题

    最大子矩阵问题的描述:
    给你一个n*m的二维数组a(该数组a又可以称为而为矩阵或简称为矩阵),则该矩阵从第0行到第n-1行共n行元素,从第0列到第m-1列共m列元素,a[i][j]即为该矩阵的第i行第j列元素。我们称以第x1行第y1列的元素为左上角元素,以x2行y2列的元素为右下角元素(其中0<=x1<=x2<=n-1,0<=y1<=y2<=m-1),所得到的所有元素的集合为矩阵a的一个子矩阵。矩阵a的最大子矩阵即为a的子矩阵中元素和最大的那一个。
    对于最大子矩阵问题,我们能够想到的最朴素的做法就是使用枚举算法进行遍历,代码如下:

    int getMaxOfMatrix2d(int a[][], int n, int m) 
    {
        int res = 0, tmp;
        for (int x1 = 0; x1 < n; x1 ++)
            for (int y1 = 0; y1 < m; y1 ++)
                for (int x2 = x1; x2 < n; x2 ++)
                    for (int y2 = y1; y2 < m; y2 ++)
                    {
                        tmp = 0;
                        for (int i = x1; i <= x2; i ++)
                            for (int j = y1; j <= y2; j ++)
                                tmp += a[i][j];
                        if (tmp > res)
                            res = tmp;
                    }
        return res;
    }
    

    然而这种方法的时间复杂度达到了O(n3*m3),作为改进,我们可以用动态规划的方法首先求一个数组sum,sum[i][j]=a[0][j]+a[1][j]+...+a[i][j],即sum的状态转移方程为sum[i][j] = sum[i-1][j] + a[i][j] (如果i==0,则sum[i][j] = a[i][j]),sum[i][j]表示矩阵a第j列的前i+1行的元素之和(即a[0][j]到a[i][j]这i+1个元素之和),sum的求解代码如下:

    for (int i = 0; i < n; i ++)
        for (int j = 0; j < m; j ++)
            if (i == 0)
                sum[i][j] = a[i][j];
            else
                sum[i][j] = sum[i-1][j] + a[i][j];
    

    求解了sum之后,我们可以只需要开一个循环就能够求解某个固定(确定了起止行x1,x2和起止列y1,y2)的子矩阵的元素和,相比之前两个循环降低了一味地时间复杂度,代码如下:

    for (int x1 = 0; x1 < n; x1 ++)
        for (int x2 = x1; x2 < n; x2 ++)
            for (int y1 = 0; y1 < m; y1 ++)
                for (int y2 = y1; y2 < m; y2 ++)
                {
                    tmp = 0;
                    for (int i = y1; i <= y2; i ++)
                        if (x1 == 0)
                            tmp += sum[x2][i];
                        else
                            tmp += sum[x2][i] - sum[x1-1][i];
                    if (tmp > res)
                        res = tmp;
                }
    

    不考虑之前的两层循环,我们可以发现里面的三层循环其实就是求一个“最大连续子序列和”(最大连续子序列和的问题我们之前讨论过,如果不记得了请记得回顾)的问题。对于一个长度为m的数组a,求其最大连续子序列和的代码如下:

    int f = 0, res = a[0];
    for (int i = 0; i < m; i ++)
    {
        if (f < 0)
            f = a[i];
        else 
            f += a[i];
        if (f > res)
            res = f;
    }
    

    如果讲代码中的a[i]用sum[x2][i] - sum[x1-1][i]表示(如果x1==0时,a[i]则用sum[x2][i]表示),带入之前讲解的求最大子矩阵的表达式,则我们可以将之前求解最大子矩阵的程序的时间复杂度简化到O(n^2*m),代码如下:

    int f, tmp, res = a[0][0];
    for (int x1 = 0; x1 < n; x1 ++)
        for (int x2 = x1; x2 < n; x2 ++)
        {
            f = 0;
            for (int i = 0; i < m; i ++)
            {
                if (x1 == 0)
                    tmp = sum[x2][i];
                else 
                    tmp = sum[x2][i] - sum[x1-1][i];
                if (f < 0)
                    f = tmp;
                else 
                    f += tmp;
                if (f > res)
                    res = f;
            }
        }
    

    对比之前的代码,我们可以发现,原本在x1到x2行之前求解最大子矩阵的时间复杂度为O(m3),现在只需要O(m)的时间复杂的,再加上枚举其实行x1和结束行x2的两层循环,该求解最大子矩阵的算法的时间复杂度为O(n2*m)。

  • 相关阅读:
    【Vegas原创】VMWare安装Linux5的注意事项
    交换机接口介质释义
    什么是IDC
    【Vegas转载】PCTFREE和PCTUSED总结
    【Vegas原创】Linux5下安装VMWare增强工具时,c header文件路径找不到解决方法
    【Vegas原创】Exchange Server 2007中设置多主机名称证书
    【Vegas原创】重建Exchange 2007 OWA的虚拟目录
    网线中的1类~6类线有什么区别
    【Vegas原创】exp时,ORA00932: 数据类型不一致解决方法
    【Vegas原创】owa映射到外网方法
  • 原文地址:https://www.cnblogs.com/xianyue/p/6895296.html
Copyright © 2011-2022 走看看