D - 最大子矩阵和
一个M*N的矩阵,找到此矩阵的一个子矩阵,并且这个子矩阵的元素的和是最大的,输出这个最大的值。
例如:3*3的矩阵:
-1 3 -1
2 -1 3
-3 1 2
和最大的子矩阵是:
3 -1
-1 3
1 2
Input
第1行:M和N,中间用空格隔开(2 <= M,N <= 500)。
第2 - N + 1行:矩阵中的元素,每行M个数,中间用空格隔开。(-10^9 <= Mii <= 10^9)
Output
输出和的最大值。如果所有数都是负数,就输出0。
Sample Input
3 3
-1 3 -1
2 -1 3
-3 1 2
Sample Output
7
首先我们可以先看一下一维数组的最大子段和,转移方程dp[i] = max ( a[j] , dp[i-1] + a[j] );
对于二维的话我们可以先将二维的转换为一维的数组然后在进行求一维的最大子段和
-1 3 -1
2 -1 3
-3 1 2
1 从第一行来看的话有三种情况
情况 1) -1 3 -1
子段和 -1 3 -1 用一维最大子段和
我们可以得到的大3 子矩阵ma[1][2]这个点;
情况 2) -1 3 -1
2 -1 3
子段和 1 2 2 最大子段和
是5,对应的矩阵是ma[1][1]为左上角,ma[2][3]为右下的点;
情况3) -1 3 -1
2 -1 3
-3 1 2
子段和 -2 3 4 最大子段和是
7对应的矩阵是ma[1][2]为左上角的点,ma[3][2]为右下角的点
2 从第二行来看有两种情况,与上面的推导过程类似;
3从第三行来看有一种情况,类似过程;
#include <bits/stdc++.h>
using namespace std;
#define rep(i,s,n) for(int i=s;i<=n;i++)
#define per(i,n,s) for(int i=n;i>=s;i--)
typedef long long ll;
const int Max =5e2+10;
ll max(ll a, ll b){
if(a>b){
return a;
}
return b;
}
ll ma[Max][Max],ch[Max][Max],a[Max],dp[Max];
int n,m;
int main(){
while(scanf("%d %d",&m,&n)!=EOF){
memset(ma,0,sizeof(ma));
rep(i,1,n){
rep(j,1,m){
scanf("%lld",&ch[i][j]);
ma[i][j]=ma[i-1][j]+ch[i][j];
}
}
ll max1=0;
rep(i,1,n){
rep(j,i,n){
rep(k,1,m){
a[k]=ma[j][k]-ma[i-1][k];
}
ll sum=0;
memset(dp,0,sizeof(dp));
rep(k,1,m){
dp[k]=max(a[k],dp[k-1]+a[k]);
sum=max(sum,dp[k]);
}
max1=max(sum,max1);
}
}
printf("%lld\n",max1);
}
return 0;
}