藍橋杯 試題 歷屆試題 最大子陣 前綴和

問題描述
  給定一個n*m的矩陣A,求A中的一個非空子矩陣,使這個子矩陣中的元素和最大。

  其中,A的子矩陣指在A中行和列均連續的一塊。
輸入格式
  輸入的第一行包含兩個整數n, m,分別表示矩陣A的行數和列數。
  接下來n行,每行m個整數,表示矩陣A。
輸出格式
  輸出一行,包含一個整數,表示A中最大的子矩陣中的元素和。
樣例輸入
3 3
-1 -4 3
3 4 -1
-5 -2 8
樣例輸出
10
樣例說明
  取最後一列,和爲10。
數據規模和約定
  對於50%的數據,1<=n, m<=50;
  對於100%的數據,1<=n, m<=500,A中每一個元素的絕對值不超過5000。

解題思路:前綴和是一種預處理,能夠下降求區間和的時間。

 一維前綴和 : sum[ i ] : a[ 1 ] + a[ 2 ] + ... + a[ i ] //數組a [ ] 下標從1開始。區間 [ L, R ]的和 = sum[ R ] - sum[ L-1 ] 

二維前綴和 sum[ i ][ j ] : 下標( i', j' )<=( i, j ) 的子矩陣和
  •二維前綴和的計算:
 
由圖能夠知道大矩形的和 = 兩個相鄰矩形和 - 重合部分 + 陰影部分矩形,用代碼表示即
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            Sum[i][j] = Sum[i-1][j] + Sum[i][j-1] + A[i][j] - Sum[i-1][j-1];    
        }    
    }

時間複雜度O(n^4)的解法:數組

有了二維前綴和,就能夠遍歷因此矩形的左上端點和右下端點,找到最大的矩形和。spa

int res = -INF;
for
(int x1=0; x1<n; x1++) { for(int y1=0; y1<m; y1++) { for(int x2=x1+1; x2<=n; x2++) { for(int y2=y1+1; y2<=m; y2++) { int t = Sum[x2][y2] - Sum[x1][y2] - Sum[x2][y1] + Sum[x1][y1];//矩形和 畫張圖就能夠理解了 res = res>t ? res : t; } } } }

但用這個方法提交後以後60分。code


時間複雜度降低爲O( n^3 )的解法。

 

爲了更好理解二維最大和矩陣解法,首先看一維怎麼計算。即求一維連續序列最大區間和。
貪心:遍歷 sum [ R ] ,每一步都取最小的 sum[ L-1 ] , 求差值更新res的值。
int Min = 0 , res = -INF;
for(int i=1; i<=n; i++ ){
   Min = min( Min, sum[ L-1 ]);
    res = max( res, sum[ R ] - Min );                  
}

二維最大區間和:blog

先求每一列的一維區間和,再遍歷行號(0 - n ) , 這時把 行號爲 i - j 的視爲一維區間,再用上面的方法求最大值。it

//代碼實現io

#include<cstdio>
#include<algorithm>
using namespace std;

const int Max_N = 500;
const int Max_M = 500;
const int INF = 100000;

//輸入
int n,m;
int A[Max_N+1][Max_M+1];

int f[Max_N+1][Max_M+1];//求一列的前綴和
int sum[Max_N+1]; //求一維最大區間和的方法

void solve()
{
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++){
            f[i][j] = f[i-1][j] + A[i][j];//一列前綴和 
        }    
    }    
    
    int res = -INF;
    for(int i=0; i<n; i++)
    {
        for(int j=i+1; j<=n; j++)//遍歷行號 
        {
            for(int k=1; k<=m; k++)//先求一維前綴和 
            {
                sum[k] = f[j][k] - f[i][k] + sum[k-1];
            }
            
            int Min = 0;
            for(int k=1; k<=m; k++)//求一維最大區間和 
            {
                Min = min( Min,sum[k-1] );
                res = max( res,sum[k]-Min );
            }
            
        }
    }
    printf("%d\n",res);
} 

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            scanf("%d",&A[i][j]);
        }
    }
    
    solve();
    
    return 0;
}
相關文章
相關標籤/搜索