参考《算法竞赛进阶指南》p46 To The Max
参考链接:https://www.acwing.com/problem/content/description/128/
https://www.cnblogs.com/GodA/p/5237061.html
https://blog.csdn.net/yzyyylx/article/details/78298318
在一个二维矩阵中求前缀和,可以快速的计算出子矩阵中的和。首先预处理处以所有点为右下角,(1,1)为左上角的矩阵中的元素和.
接着(x1,y1)为右下角,(x2,y2)为左上角的矩形中的元素和为f[x1][y1]+f[x2-1][y2-1]-f[x1][y2-1]-f[x2-1][y1].
如上矩阵中。
#include<bits/stdc++.h> #define MAXM 3010 #define MAXN 3010 using namespace std; int m,n,a[MAXM][MAXN],jx[MAXM][MAXN],x,y,u,v; int main() { int i,j; cin>>m>>n; for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { scanf("%d",&a[i][j]); } } //预处理: for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { jx[i][j]=a[i][j]+jx[i-1][j]+jx[i][j-1]-jx[i-1][j-1]; } } while(~scanf("%d%d%d%d",&u,&v,&x,&y)) { printf("%d\n",jx[u][v]+jx[x-1][y-1]-jx[u][y-1]-jx[x-1][v]); } }
/*
5 5
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
1 1
0 0
1
2 2
1 1
16
4 4
3 3
64
*/
输入右下角和左上角的值就能够算出子矩阵的和
题目大意:就是输入一个N*N的矩阵,找出在矩阵中,所有元素加起来之和最大的子矩阵。
例如在 0 -2 -7 0 这样一个4*4的矩阵中,元素之和最大的子矩阵为 9 2 ,它们之和为15。
9 2 -6 2 -4 1
-4 1 -4 1 -1 8
-1 8 0 -2
这是一个最大子矩阵问题,我们怎么来解决这个问题呢?任何问题都会有它的简化的问题,这是二维的数组,与之对应的,我们可以先尝试一下一维数组。
如果有一个一维数组a[n],如何找出连续的一段,使其元素之和最大呢?
例如有 1 2 -3 4 -2 5 -3 -1 7 4 -6 这样一个数组,那么显然 4 -2 5 -3 -1 7 4 这个子数组元素之和最大,为4+(-2)+5+(-3)+(-3)+7+4=14。为找到一维数组的最大子数组,我们可以有以下方法。
动态规划:我们来分析一下最优子结构,若想找到n个数的最大子段和,那么要找到n-1个数的最大子段和,这就出来了。我们用b[i]来表示a[0]...a[i]的最大子段和,b[i]无非有两种情况:
(1)最大子段一直连续到a[i]
(2)以a[i]为首的新的子段。
由此我们可以得到b[i]的状态转移方程:b[i]=max{b[i-1]+a[i],a[i]}。最终我们得到的最大子段和为max{b[i], 0<=i<n}, 算法如下:
int MaxSubArray(int a[],int n) { int i,b = 0,sum = 0; for(i = 0;i < n;i++) { if(b>0) // 若a[i]+b[i-1]会减小 b += a[i]; // 则以a[i]为首另起一个子段 else b = a[i]; if(b > sum) sum = b; } return sum; }
回到最大子矩阵问题,我们固定矩阵的左右边界,利用二维前缀和很容易求出夹在左右边界的小矩阵的值。代码如下;
#include<bits/stdc++.h> #define MAXM 3010 #define MAXN 3010 using namespace std; int m,n,a[MAXM][MAXN],jx[MAXM][MAXN]; int main() { int i,j; cin>>n; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { scanf("%d",&a[i][j]); } } //预处理: for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { jx[i][j]=a[i][j]+jx[i-1][j]+jx[i][j-1]-jx[i-1][j-1]; } } int res=-10000; for (int i = 1; i <=n; ++i) { for (int j = i; j <=n; ++j) { int last=0; for (int k = 0; k < n; ++k) { if(last>0) last+=jx[j][k]+jx[i-1][k-1]-jx[j][k-1]-jx[i-1][k]; else last=jx[j][k]+jx[i-1][k-1]-jx[j][k-1]-jx[i-1][k]; // last=max(last,0)+jx[j][k]+jx[i-1][k-1]-jx[j][k-1]-jx[i-1][k]; res=max(last,res); } } } cout<<res; }