• 二维前缀和之最大的和


    参考《算法竞赛进阶指南》p46 To The Max

    参考链接:https://www.acwing.com/problem/content/description/128/

           https://www.cnblogs.com/GodA/p/5237061.html

           https://blog.csdn.net/yzyyylx/article/details/78298318

     黑色方块=D-B-C-A;

    class NumMatrix {
    public:
        int preSum[10005][100005];
    
        NumMatrix(vector<vector<int>> &matrix) {
        // 设定一个辅助数组记录 (0,0) 到 (row,col) 子矩阵的元素之和
        // 为了简化代码,辅助数组的长度设置为 (matrix.length + 1)  * (matrix[0].length + 1)
        // 第一行和第一列不放数据
    
        // (0,0) 到 (i,j) 子矩阵的元素和
        // preSum[i+1][j+1] = matrix[i][j] + preSum[i][j+1] + preSum[i+1][j] - preSum[i][j]
            for (int i = 0; i < matrix.size(); i++) {
                for (int j = 0; j < matrix[0].size(); j++) {
                    preSum[i + 1][j + 1] = matrix[i][j] + preSum[i][j + 1] + preSum[i + 1][j] - preSum[i][j];
                }
            }
    
        }
    
        int sumRegion(int row1, int col1, int row2, int col2) {
            return preSum[row2+1][col2+1] - preSum[row2+1][col1] - preSum[row1][col2+1] + preSum[row1][col1];
        }
    };
    

      

    在一个二维矩阵中求前缀和,可以快速的计算出子矩阵中的和。首先预处理处以所有点为右下角,(1,1)为左上角的矩阵中的元素和. 
    接着(x1,y1)为右下角,(x2,y2)为左上角的矩形中的元素和为f[x1][y1]+f[x2-1][y2-1]-f[x1][y2-1]-f[x2-1][y1].

    如上矩阵中。

    #include<bits/stdc++.h>
    #define MAXM 3010
    #define MAXN 3010
    using namespace std;
    
    int m,n,a[MAXM][MAXN],jx[MAXM][MAXN],x,y,u,v;
    
    int main()
    {
        int i,j;
        cin>>m>>n;
        for(i=1;i<=m;i++)
        {
            for(j=1;j<=n;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
    
        //预处理: 
        for(i=1;i<=m;i++)
        {
            for(j=1;j<=n;j++)
            {
                jx[i][j]=a[i][j]+jx[i-1][j]+jx[i][j-1]-jx[i-1][j-1];
            }
        }
    
        while(~scanf("%d%d%d%d",&u,&v,&x,&y))
        {
            printf("%d
    ",jx[u][v]+jx[x-1][y-1]-jx[u][y-1]-jx[x-1][v]);
        }
    }
    /*
    5 5
    1 2 3 4 5
    6 7 8 9 10
    11 12 13 14 15
    16 17 18 19 20
    21 22 23 24 25
    1 1
    0 0
    1
    2 2
    1 1
    16
    4 4
    3 3
    64
     */

     

    输入右下角和左上角的值就能够算出子矩阵的和

    题目大意:就是输入一个N*N的矩阵,找出在矩阵中,所有元素加起来之和最大的子矩阵。

      例如在    0 -2 -7 0 这样一个4*4的矩阵中,元素之和最大的子矩阵为   9 2  ,它们之和为15。 

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

      这是一个最大子矩阵问题,我们怎么来解决这个问题呢?任何问题都会有它的简化的问题,这是二维的数组,与之对应的,我们可以先尝试一下一维数组。

      如果有一个一维数组a[n],如何找出连续的一段,使其元素之和最大呢?

      例如有 1 2 -3 4 -2 5 -3 -1 7 4 -6 这样一个数组,那么显然 4 -2 5 -3 -1 7 4 这个子数组元素之和最大,为4+(-2)+5+(-3)+(-3)+7+4=14。为找到一维数组的最大子数组,我们可以有以下方法。

    动态规划:我们来分析一下最优子结构,若想找到n个数的最大子段和,那么要找到n-1个数的最大子段和,这就出来了。我们用b[i]来表示a[0]...a[i]的最大子段和,b[i]无非有两种情况:

    (1)最大子段一直连续到a[i]  

    (2)以a[i]为首的新的子段。

    由此我们可以得到b[i]的状态转移方程:b[i]=max{b[i-1]+a[i],a[i]}。最终我们得到的最大子段和为max{b[i], 0<=i<n}, 算法如下:

    int MaxSubArray(int a[],int n)
    {
        int i,b = 0,sum = 0;
        for(i = 0;i < n;i++)
        {
            if(b>0)                // 若a[i]+b[i-1]会减小
                b += a[i];        // 则以a[i]为首另起一个子段
            else    
                b = a[i];
            if(b > sum)    
                sum = b;
        }
        return sum;
    }
    

    回到最大子矩阵问题,我们固定矩阵的左右边界,利用二维前缀和很容易求出夹在左右边界的小矩阵的值。代码如下;

    #include<bits/stdc++.h>
    #define MAXM 3010
    #define MAXN 3010
    using namespace std;
    
    int m,n,a[MAXM][MAXN],jx[MAXM][MAXN];
    
    int main()
    {
        int i,j;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
    
        //预处理:
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                jx[i][j]=a[i][j]+jx[i-1][j]+jx[i][j-1]-jx[i-1][j-1];
            }
        }
    
        int res=-10000;
        for (int i = 1; i <=n; ++i) {
            for (int j = i; j <=n; ++j) {
                int last=0;
                for (int k = 0; k < n; ++k) {
                    if(last>0)
                        last+=jx[j][k]+jx[i-1][k-1]-jx[j][k-1]-jx[i-1][k];
                    else
                        last=jx[j][k]+jx[i-1][k-1]-jx[j][k-1]-jx[i-1][k];
                   // last=max(last,0)+jx[j][k]+jx[i-1][k-1]-jx[j][k-1]-jx[i-1][k];
                    res=max(last,res);
                }
    
            }
        }
        cout<<res;
    }
    

      

    加油啦!加油鸭,冲鸭!!!
  • 相关阅读:
    问卷调查
    20145104张家明 《Java程序设计》第6周学习总结
    20145104张家明 《Java程序设计》第2周学习总结
    20145104张家明 《Java程序设计》第一周学习总结
    20145104张家明 《Java程序设计》第3周学习总结
    socketserver OSError:[Errno 98] Address already in use
    psycopg2.OperationalError: FATAL: password authentication failed for user "postgres"
    nginx 配置nginx.conf
    css 一些常用属性总结
    在ie中用滤镜 (filter:progid:DXImageTransform.Microsoft.gradient)会触发overflow:hidden?
  • 原文地址:https://www.cnblogs.com/clarencezzh/p/10610785.html
Copyright © 2020-2023  润新知