参考《算法竞赛进阶指南》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;
}