首页 > 试题广场 >

子矩阵的最大累加和问题

[编程题]子矩阵的最大累加和问题
  • 热度指数:2468 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定一个矩阵matrix,其中的值有正、有负、有0,返回子矩阵的最大累加和
例如,矩阵matrix为:
-90 48 78
64 -40 64
-81 - 7 66
其中,最大的累加和的子矩阵为:
48 78
-40 64
-7 66
所以返回累加和209。
例如,matrix为:
-1 -1 -1
-1 2 2
-1 -1 -1
其中,最大累加和的子矩阵为:
2 2
所以返回4
[要求]
时间复杂度为,空间复杂度为


输入描述:
第一行有两个整数N,M。分别表示矩阵的行数/列数
接下来N行,每行M个整数表示矩阵内的数


输出描述:
输出一个整数表示答案
示例1

输入

3 3
-90 48 78
64 -40 64
-81 -7 66 

输出

209 
示例2

输入

3 3
-1 -1 -1
-1 2 2
-1 -1 -1 

输出

4 

备注:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define max(a,b) ((a) > (b) ? (a) : (b))

int main(void) {
    int n, m, **matrix;
    scanf("%d%d", &n, &m);
    matrix = (int **) malloc(n * sizeof(int *));
    for (int i = 0; i < n; i++) {
        matrix[i] = (int *) malloc(m * sizeof(int));
        for (int j = 0; j < m; j++) {
            scanf("%d", &matrix[i][j]);
        }
    }
    
    int *help, pre, maxSum = INT_MIN;
    for (int i = 0; i < n; i++) {
        help = (int *) calloc(m, sizeof(int));
        for (int j = i; j < n; j++) {
            pre = 0;
            for (int k = 0; k < m; k++) {
                help[k] += matrix[j][k];
                pre = help[k] + pre;
                maxSum = max(maxSum, pre);
                pre = pre < 0 ? 0 : pre;
            }
        }
    }
    printf("%d\n", maxSum);
    return 0;
}

发表于 2022-04-04 21:30:27 回复(0)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int m,n;
    cin>>m>>n;
    int a[m][n], b[n], s=INT_MIN;
    for(int i=0;i<m;i++)
        for(int j=0;j<n;j++)
            cin>>a[i][j];
    for(int i=0;i<m;i++){
        memset(b, 0, sizeof(b));
        for(int j=i;j<m;j++){
            for(int k=0;k<n;k++)
                b[k] += a[j][k];
            for(int k=0,t=0;k<n;k++){
                if(k==0 || t<0)
                    t = b[k];
                else
                    t += b[k];
                s = max(s, t);
            }
        }
    }
    cout<<s<<endl;
    return 0;
}

发表于 2020-02-18 00:07:47 回复(0)
O(nm)的空间缓存列前缀和presumpresum[i]表示原始矩阵从第0行到第i行的累加和向量。要求任意子矩阵的累加和,我们只需要先求出presum[j]-presum[i],即为原始矩阵第i+1行到第j行的累加和,划定好行的范围,然后在列的维度上求解一维数组的最大累加和就可以了。遍历所有的行范围,在列上求得的最大数组累加和中的最大值就是我们要的答案。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] params = br.readLine().split(" ");
        int n = Integer.parseInt(params[0]), m = Integer.parseInt(params[1]);
        int[][] arr = new int[n][m];
        for(int i = 0; i < n; i++){
            String[] line = br.readLine().split(" ");
            for(int j = 0; j < m; j++)
                arr[i][j] = Integer.parseInt(line[j]);
        }
        System.out.println(maxCumsum4Mat(arr));
    }
    
    private static int maxCumsum4Mat(int[][] presum) {
        int[] zeros = new int[presum[0].length];
        int maxSum = Integer.MIN_VALUE;
        // 先计算列上面的前缀和
        for(int i = 1; i < presum.length; i++){
            for(int j = 0; j < presum[0].length; j++)
                presum[i][j] += presum[i - 1][j];
        }
        for(int end = 0; end < presum.length; end++){
            if(end != 0){
                // 计算arr[start:end][:]上的最大子矩阵累加和
                for(int start = -1; start < end; start++)
                    maxSum = Math.max(maxSum, maxCumsum4Vec(arrSubtract(presum[end], start >= 0? presum[start]: zeros)));
            }else
                maxSum = Math.max(maxSum, maxCumsum4Vec(presum[end]));
        }
        return maxSum;
    }
    
    private static int maxCumsum4Vec(int[] arr) {
        int max = Integer.MIN_VALUE;
        int dp = arr[0];
        for(int i = 1; i < arr.length; i++){
            dp = dp > 0? dp + arr[i]: arr[i];     // 前面的贡献是正的就加上arr[i],否则就从arr[i]重新开始算
            max = Math.max(max, dp);
        }
        return max;
    }
    
    private static int[] arrSubtract(int[] v1, int[] v2){
        for(int i = 0; i < v1.length; i++) v1[i] -= v2[i];
        return v1;
    }
}

发表于 2021-11-27 12:22:34 回复(0)
这道题和前一道数组求最大和子数组是一脉相承的,那道题是这道题的题母。这道题,先把不管1行,2行,3行,,,,,,的数组上下相加,变成一位数组,然后再套用那个模型。最后再比较几行的情况最好

import java.util.*;


public class Main {
    public static void main(String args[])
    {
        Scanner cin = new Scanner(System.in);
        int n,m;
        while(cin.hasNextInt())
        {
            n = cin.nextInt();
            m=cin.nextInt();
            int[][] a=new int[n][m];
            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++){
                    a[i][j]=cin.nextInt();
                }
            }

            int res=findMaxSum(a);
            System.out.println(res);

        }
    }

    public static int findMaxSum(int[][] arr){


        //起始行
        int[] help;
        int max=Integer.MIN_VALUE;
        for(int i=0;i<arr.length;i++){
            help=new int[arr[0].length];
            //终止行
            for(int j=i;j<arr.length;j++){

                int res=0;
                for(int k=0;k<help.length;k++){
                    help[k]=help[k]+arr[j][k];
                    res+=help[k];
                    if(res<0){
                        res=0;
                    }
                    max=Math.max(res,max);
                }
            }
        }
        return max;
    }

}

发表于 2020-08-06 11:09:38 回复(0)
以O(m^2)的复杂度遍历所有子矩阵可能的左右端点。对于每个遍历过程,将每一行的对应范围内的值相加得到一个数,所有行在左右范围内的值变为一个数组,求其最大连续子数组的和

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <climits>

using namespace std;

int main()
{
    cin.tie();
    ios::sync_with_stdio(false);
    
    int n, m;
    cin >> n >> m;
    
    vector<vector<int>> inp(n + 1, vector<int>(m + 1, 0));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) cin >> inp[i][j];
    }
    
    // 预处理前缀和
    vector<vector<int>> pre(n + 1, vector<int>(m + 1, 0));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            pre[i][j] = pre[i][j - 1] + inp[i][j];
        }
    }
    
    int res = INT_MIN;
    // 遍历所有可能的子矩阵左右端点,每次遍历根据预处理的前缀和求数组的最大连续子数组
    for (int l = 1; l <= m; l++) {
        for (int r = l; r <= m; r++) {
            // 求最大连续子数组
            int tmp = 0;
            for (int i = 1; i <= n; i++) {
                int now = pre[i][r] - pre[i][l - 1];
                if (tmp < 0) {
                    tmp = now;
                } else {
                    tmp += now;
                }
                res = max(res, tmp);
            }
        }
    }
    
    cout << res << endl;
    
    return 0;
}


发表于 2019-12-17 15:42:48 回复(0)
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[][] matrix = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                matrix[i][j] = sc.nextInt();
            }
        }

        int[] s = null;
        int cur = 0;
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < matrix.length; i++) {
            s = new int[matrix[0].length];
            for (int j = i; j < matrix.length; j++) {
                cur = 0;
                for (int k = 0; k < matrix[0].length; k++) {
                    s[k] += matrix[j][k];
                    cur += s[k];
                    max = Math.max(max, cur);
                    if (cur < 0) {
                        cur = 0;
                    }
                }
            }
        }
        System.out.println(max);
    }
}
发表于 2019-10-11 18:59:09 回复(0)
26题的双维变形
#include "bits/stdc++.h"
using namespace std;
int row_max(vector<int>& arr){
    int len=arr.size();
    int min_pre=0;
    int prefix=0;
    int ret=0;
    for(int i=0;i<len;i++){
        prefix+=arr[i];
        int tmp=prefix-min_pre;
        ret=max(ret,tmp);
        min_pre=min(min_pre,prefix);
    }
    return ret;
}
int main(){
    int n,m;cin>>n>>m;
    vector<vector<int>> matrix(n,vector<int>(m,0));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>matrix[i][j];
        }
    }
    vector<int> arr(m,0);
    int tmp=row_max(matrix[0]);
    int ret=max(0,tmp);
    for(int i=1;i<n;i++){
        for(int j=0;j<m;j++){
            matrix[i][j]+=matrix[i-1][j];
        }
        tmp=row_max(matrix[i]);
        ret=max(ret,tmp);
        for(int ii=0;ii<i;ii++){
            for(int jj=0;jj<m;jj++){
                arr[jj]=matrix[i][jj]-matrix[ii][jj];
            }
            tmp=row_max(arr);
            ret=max(ret,tmp);
        }
    }
    cout<<ret;
    return 0;
}


发表于 2022-07-09 22:49:45 回复(0)
这道题标简单就离谱
发表于 2021-11-10 09:04:27 回复(0)
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int row = sc.nextInt();
        int col = sc.nextInt();
        int[][] m = new int[row][col];
        for(int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                m[i][j] = sc.nextInt();
            }
        }
        System.out.println(getMaxSum(m));
        sc.close();
    }
    
    //本题主旨在通过把多行合并和一列数组,然后再本列数组中找最大累加和从而得到结果
    //比方说合并第一行与第二行时,含义为,限定子矩阵必须为两行(第一行与第二行)时,
    //最大累加和是多少,以此类推,把所有可能都找个遍即可
    public static int getMaxSum(int[][] m) {
        if (m == null || m[0] == null || m.length == 0 || m[0].length == 0)
            return 0;
        int max = Integer.MIN_VALUE;
        int[] tmp = null;
        int cur = 0;
        for (int i = 0; i < m.length; i++) {
            tmp = new int[m[0].length];
            for (int j = i; j < m.length; j++) {
                cur = 0;
                //在一个数组中找最大累加和的做法
                for (int k = 0; k < m[0].length; k++) {
                    tmp[k] += m[j][k];
                    cur += tmp[k];
                    max = Math.max(max, cur);
                    //无论接下来的数是正是负,都没必要多加上一个负的累加和
                    cur = cur < 0 ? 0 : cur;
                }
            }
        }
        return max;
    }
}

发表于 2021-08-21 01:23:17 回复(0)
import java.util.Scanner;

public class Main {
    private static int maxSubArraySum(int[] arr) {
        int sum = 0;
        int max = Integer.MIN_VALUE;
        for (int num : arr) {
            sum += num;
            max = Math.max(max, sum);
            if (sum < 0) {
                sum = 0;
            }
        }
        return max;
    }

    private static int maxSubMatrixSum(int[][] matrix) {
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < matrix.length; i++) {
            int[] array = matrix[i];
            max = Math.max(max, maxSubArraySum(array));
            for (int k = 1; i + k < matrix.length; k++) {
                for (int j = 0; j < matrix[0].length; j++) {
                    array[j] += matrix[i + k][j];
                }
                max = Math.max(max, maxSubArraySum(array));
            }
        }
        return max;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[][] matrix = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                matrix[i][j] = sc.nextInt();
            }
        }
        int res = maxSubMatrixSum(matrix);
        System.out.println(res);
    }
}
发表于 2021-04-28 17:25:33 回复(0)
import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String[] str1 = bf.readLine().split(" ");
        int n = Integer.valueOf(str1[0]), m = Integer.valueOf(str1[1]);
        int[][] arr = new int[n][m];
        String[] str2;
        for (int i = 0; i < n; i++) {
            str2 = bf.readLine().split(" ");
            for (int j = 0; j < m; j++) {
                arr[i][j] = Integer.valueOf(str2[j]);
            }
        }
        bf.close();

        int[] temp;
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++) {
            temp = new int[m];
            for (int j = i; j >= 0; j--) {
                for (int k = 0; k < m; k++) {
                    temp[k] += arr[j][k];
                }
                max = Math.max(max, helper(temp));
            }
        }
        System.out.println(max);
    }

    public static int helper(int[] temp) { // 计算一维数组的子数组的最大累加和,当然可以写入上面三层for循环中的最内层for循环
        int sum = 0, max = 0;
        for (int i = 0; i < temp.length; i++) {
            if (sum >= 0) {
                sum += temp[i];
            } else {
                sum = temp[i];
            }
            max = Math.max(max, sum);
        }
        return max;
    }
}


编辑于 2020-06-04 23:40:08 回复(0)
#include<cstdio>
(802)#include<algorithm>
using namespace std;
int v[210][210];
int r[210];
int n,m;
int ssum(){
    int mmax,dp;
    dp=r[0];
    mmax=r[0];
    for(int i=1;i<m;++i){
        dp=dp<0?r[i]:(dp+r[i]);
        mmax=max(mmax,dp);
    }
    return mmax;
}
int main(){
     scanf("%d %d",&n,&m);
     for(int i=0;i<n;++i){
         for(int j=0;j<m;++j){
             scanf("%d",&v[i][j]);
         }
     }
    int maxsum=-110;
    for(int i=0;i<n;++i){
        fill(r,r+m,0);
        for(int j=i;j<n;++j){
            for(int k=0;k<m;++k){
                r[k]+=v[j][k];
            }
            maxsum=max(maxsum,ssum());
        }
    }
    printf("%d",maxsum);
    return 0;
}

发表于 2020-03-23 15:09:49 回复(0)