#include<stdio.h> #include<string.h> int a[110][110],n; int maxx(int *b,int m) { int i,max,now; for(i=0;i<m;i++) { if(i==0) { now=max=b[i]; continue; } if(now+b[i]>b[i]) now+=b[i]; else now=b[i]; if(max<now) max=now; } return max;//计算不同的行之间的相同的列之和的最大和,即一个矩阵 } int maxpp() { int b[11111],i,j,sum=-999999,max,k; for(i=0;i<n;i++)//将所有的行枚举一遍 { memset(b,0,sizeof(b)); for(j=i;j<n;j++)//这里仿造最大字段和的写法 { for(k=0;k<n;k++) { b[k]+=a[j][k];//b[k]为纵向的列的和,这样每个b[k]对应的行,列就都一样了,就可以进行相加比较了 max=maxx(b,k+1); if(sum<max)sum=max;//这三行之所以放在同一的循环里面就是为了 每一纵列都能进行一次取最大值 } } } return sum; } int main() { int i,j; while(scanf("%d",&n)!=EOF) { for(i=0;i<n;i++) for(j=0;j<n;j++) scanf("%d",&a[i][j]); printf("%d\n",maxpp()); } return 0; }
http://acm.hdu.edu.cn/showproblem.php?pid=1081
给出一个矩阵,求出最大的子矩阵的和。思路其实只是从一维的最大字段和转化为二维的而已。在一维的情况下,每一次横向进行判断的数只是一个,而在二维的情况下,每一次横向进行判断的应该是整个纵列的和,而每个纵列的行又是一次纵向的判断,,所以其实就是在纵向的进行一次最大子段求和的循环里面进行横向的最大子段求和
题目可以转化为两个子问题:
1,给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a+a+…+a[j]的子段和的最大值。当所给的整均为负数时定义子段和为0,依此定义,所求的最优值为Max{0,a+a+…+a[j]},1<=i<=j<=n
我们把b[j]理解为从前面某项开始包含a[j](a[j]为最后一个元素)的连续的段的和最大值,易知,开始的a[i]必定为正数!
记b[j]=max(a++a[j-1]+a[j]),其中1<=i<=j,并且1<=j<=n,需要明确的一点是b[j]的值必须包含a[j].则所求的最大子段和为max{b[j]},1<=j<=n.
由b[j]的定义可易知,当b[j-1]>0时(即前面的段加上a[j]这一段值会更大,自然把a[j]这一段接上)b[j]=b[j-1]+a[j],否则(由于前面的段为负值,加上a[j],会使值变小,这样的话,我们将前面的段去掉,a[j]即是最大值)b[j]=a[j].故b[j]的动态规划递归式为 b[j]=max(b[j-1]+a[j],a[j]),1<=j<=n.
2、最大子矩阵和
矩阵是二维的,将其压缩成一维就可以用上面的方法求出最大子矩阵和了
即将一层层的数相加,压缩成一维的即可,我们遍历所有的压缩可能,就可以通过上面的方法,求出最大值!