【题目描述】
一个N*M的矩阵,每个格子里面有个整数( 绝对值不大与10 ) ,每个子矩阵( 至少包含一个元素 )的价值就是它所包含的格子内的数的和。 现在求两个不相交的子矩阵(不包含相同的格子),使得他们的价值的乘积最大。
例如: N=3 , M=4,矩阵如图所示:2 3 4 5
1 3 2 4
4 3 2 1
最大子矩阵值乘积为288。(左边两列的和为16,右边两列的和为18,结果为16*18=288)。
【输入格式】
第一行有两个数字n, m ( n, m < 100)。以后的n行,每行有m个整数。
【输出格式】
输出文件只有一个数,即两不相交子矩阵价值乘积的最大值。
【样例输入】 【样例输出】
1 7
128
-9 -9 8 8 1 7 -4
最大子矩阵扩展题目,注意负数的情况,任何两个不重合子矩阵,必定被一条横轴或竖轴分开,我们通过枚举横线与竖线,通过n^3预处理矩阵,可以使复杂度降到O(n^3).
//f【i】表示从第1行到第i行,宽度为m的最大子矩阵
//dp【i】表示从第1列到第i列,长度为n的最大子矩阵
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,s[105][105],f[105],f2[105],f3[105],f4[105],dp[105],dp2[105],dp3[105],dp4[105],a[105],s2[105][105],ans=-0x3f3f3f3f;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int x;
scanf("%d",&x);
s[i][j]=s[i-1][j]+x;
s2[i][j]=s2[i][j-1]+x;
}
f[0]=-0x3f3f3f3f;
f2[0]=0x3f3f3f3f;
f3[n+1]=-0x3f3f3f3f;
f4[n+1]=0x3f3f3f3f;
dp[0]=-0x3f3f3f3f;
dp2[0]=0x3f3f3f3f;
dp3[m+1]=-0x3f3f3f3f;
dp4[m+1]=0x3f3f3f3f;//初始化
for(int i=1;i<n;i++)
{
f[i]=f[i-1];//通过继承前一次的状态,我们可以优化掉一个循环,每一次必定大于等于前一次的情况,只需要枚举增加的情况即可
for(int x=1;x<=i;x++)
{
a[1]=s[i][1]-s[x-1][1];
f[i]=max(f[i],a[1]);
for(int k=2;k<=m;k++)
{
a[k]=max(a[k-1]+s[i][k]-s[x-1][k],s[i][k]-s[x-1][k]);
f[i]=max(f[i],a[k]);
}
}
}//处理最大情况
for(int i=n;i>1;i--)
{
f3[i]=f3[i+1];
for(int x=n;x>=i;x--)
{
a[1]=s[x][1]-s[i-1][1];
f3[i]=max(f3[i],a[1]);
for(int k=2;k<=m;k++)
{
a[k]=max(a[k-1]+s[x][k]-s[i-1][k],s[x][k]-s[i-1][k]);
f3[i]=max(f3[i],a[k]);
}
}
}//反向处理最大情况
for(int i=1;i<n;i++)
{
f2[i]=f2[i-1];
for(int x=1;x<=i;x++)
{
a[1]=s[i][1]-s[x-1][1];
f2[i]=min(f2[i],a[1]);
for(int k=2;k<=m;k++)
{
a[k]=min(a[k-1]+s[i][k]-s[x-1][k],s[i][k]-s[x-1][k]);
f2[i]=min(f2[i],a[k]);
}
}
}//处理最小情况
for(int i=n;i>1;i--)
{
f4[i]=f4[i+1];
for(int x=n;x>=i;x--)
{
a[1]=s[x][1]-s[i-1][1];
f4[i]=min(f4[i],a[1]);
for(int k=2;k<=m;k++)
{
a[k]=min(a[k-1]+s[x][k]-s[i-1][k],s[x][k]-s[i-1][k]);
f4[i]=min(f4[i],a[k]);
}
}
}//反向处理最小情况,dp数组相同
for(int i=1;i<m;i++)
{
dp[i]=dp[i-1];
for(int x=1;x<=i;x++)
{
a[1]=s2[1][i]-s2[1][x-1];
dp[i]=max(dp[i],a[1]);
for(int k=2;k<=n;k++)
{
a[k]=max(a[k-1]+s2[k][i]-s2[k][x-1],s2[k][i]-s2[k][x-1]);
dp[i]=max(dp[i],a[k]);
}
}
}
for(int i=m;i>1;i--)
{
dp3[i]=dp3[i+1];
for(int x=m;x>=i;x--)
{
a[1]=s2[1][x]-s2[1][i-1];
dp3[i]=max(dp3[i],a[1]);
for(int k=2;k<=n;k++)
{
a[k]=max(a[k-1]+s2[k][x]-s2[k][i-1],s2[k][x]-s2[k][i-1]);
dp3[i]=max(dp3[i],a[k]);
}
}
}
for(int i=1;i<m;i++)
{
dp2[i]=dp2[i-1];
for(int x=1;x<=i;x++)
{
a[1]=s2[1][i]-s2[1][x-1];
dp2[i]=min(dp2[i],a[1]);
for(int k=2;k<=n;k++)
{
a[k]=min(a[k-1]+s2[k][i]-s2[k][x-1],s2[k][i]-s2[k][x-1]);
dp2[i]=min(dp2[i],a[k]);
}
}
}
for(int i=m;i>1;i--)
{
dp4[i]=dp4[i+1];
for(int x=m;x>=i;x--)
{
a[1]=s2[1][x]-s2[1][i-1];
dp4[i]=min(dp4[i],a[1]);
for(int k=2;k<=n;k++)
{
a[k]=min(a[k-1]+s2[k][x]-s2[k][i-1],s2[k][x]-s2[k][i-1]);
dp4[i]=min(dp4[i],a[k]);
}
}
}
for(int i=1;i<n;i++)
{
ans=max(ans,max(f[i]*f3[i+1],f2[i]*f4[i+1]));
}//枚举横线
for(int i=1;i<m;i++)
{
ans=max(ans,max(dp[i]*dp3[i+1],dp2[i]*dp4[i+1]));
}//枚举竖线
printf("%d",ans);
}