【DP】【最大矩形】

【题目描述】

一个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);
}


猜你喜欢

转载自blog.csdn.net/dy_dream/article/details/80621787