问题描述

  给定一个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。

因为要求所有的行列都是连续的,因此我们枚举所有可能情况就行了,但这里有一个技巧,我们可以提前处理好每一列的前n行的和,然后枚举的时候只需枚举一个行的开始,和行的结束,然后对这个范围的所有列(即m的值)进行最大子段和的dp就OK啦,这一过程中不断维护最大值;
其实最后还是转化为求一维数组的最大子段和的最大值

虽然这道题比较简单,但是比较典型,也算是个DP吧。。。

#include <iostream>
#include <cstring>
using namespace std;
#define inf 0x3f3f3f3f;
int n,m;
int a[505][505],b[505],dp[505];
int res=-inf;
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
	   for(int j=0;j<m;j++)
	      cin>>a[i][j];
    for(int x=0;x<n;x++){//设置起始行 
        memset(b,0,sizeof(b));//设置结束行 
        for(int y=x;y<n;y++){
            for(int j=0;j<m;j++)
			    b[j]+=a[y][j];//b存储第起始行x到y行到中每一列的值 
            dp[0]=b[0];
            
            if(dp[0]>res)//不要忘了这个 
			    res=dp[0];
			    
            for(int i=1;i<m;i++){
                if(dp[i-1]<0)
				    dp[i]=b[i];
                else 
				   dp[i]=dp[i-1]+b[i];
				  	 
                if(dp[i]>res)          //维护最后的结果 
				   res=dp[i];
            }
  
        }
    }
    cout<<res<<endl;
    return 0;
}


开始找到一种比较笨拙的类似方法,不过使用了sum这个数组的中间变量来记录前i行第j列的和,开始看到这个代码好有误解性啊。

#include <iostream>
#include <stdio.h>
#define N 510
#define INF 0x3f3f3f3f  //无穷大 
using namespace std;
int n, m,map[N][N],sum[N][N];

int mymax(int *p)//从i行到j行的临时数组temp求和(dp) 
{
    int b=0,max=-INF;
    for(int i=0;i<m;i++) {
        b+=p[i];
        if(b>max)   max=b;
        if(b<0)    b=0;
    }
    return max;
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=0;i<n;i++) {
        for (int j=0;j<m;j++){
            scanf("%d", &map[i][j]);
            if (i==0)
                sum[i][j]=map[0][j];
            else 
                sum[i][j]=sum[i-1][j]+map[i][j];//sum中存储前i行第j列的和 
        }
    }
    int max=-INF;   //无穷小
    for (int i=0;i<n;i++) {//设置起始行 
        for (int j=i;j<n;j++) {//设置结束行 
            int temp[N];
            for (int k=0;k<m;k++){ //b存储第起始行y行到当前行的值   
               if (i==0) 
			     temp[k]=sum[j][k];
               else
			     temp[k]=sum[j][k]-sum[i-1][k];
            }
			int xx=mymax(temp);
            if ( xx>max)
                max=xx;
		}
    }
    printf("%d\n", max);
    return 0;
}