最近模拟赛略频繁,导致博客有点断片,自己的计划有点停滞不前,以此来督促一下自己。
题目中说两个矩阵不可以相交,那么这两个矩阵肯定就可以被一条线在原来的矩阵中分开,当然可能是横线也可能是竖线。所以我们可以枚举这条分割线,最多枚举200次,这样枚举的前提就是我们已经求出了分割线两边的大矩阵中的和最大的子矩阵。
那么就是,给出分割线i,以竖直的为例,我们要求的是(1,1)-(n,i)这个大矩阵中的和最大的子矩阵,这里可以用DP的思想。我们设sum[i][0]表示总矩阵第i列至左端这个矩阵中最大子矩阵和,sum[i][1]表示第i列至最右端的最大和。pre[i][j][k]表示以(j,i)-(k,i)为最右边一条边的矩阵中和的最大值(画一画就很形象了,原谅我的文字描述很难看)。pre[i][j][k] = max(pre[i-1][j][k], 0) + col[i][k] - col[i][j],其中col[i][j]表示第i列的前j个之和。我们需要枚举的只有i,j,k,也就是O(n^3)。sum[i][0] = max{pre[i][j][k]}(j <= k <= n)。类似地,我们可以求出sum[i][1]。于是有 ans = max{sum[i][0]*sum[i+1][1]}。
类似地,在使分割线横着求一遍sum[i][0]和sum[i][1]即可,就是sum[i][0]表示第i行以上的最大子矩阵和。
当然这还不够,因为可能两个和都是负数,最后相乘得到正数,这就需要我们再作一次上面的工作,仅仅把求pre数组中的max改为min就好。
自己丑陋的代码,四个过程(竖线-正,竖线-负,横线-正,横线-负),复制粘贴了三次。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, m, a[101][101];
long long pre[101][101][101], suf[101][101][101], col[101][101], sum[101][2];
long long ans = -(1<<30), zero = 0;
int main()
{
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
scanf("%d", &a[i][j]);
// NO.1
memset(sum, -0x3f, sizeof sum);
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
col[i][j] = col[i][j-1]+a[j][i];
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
for(int k = j; k <= n; k++){
pre[i][j][k] = max(pre[i-1][j][k], zero)+col[i][k]-col[i][j-1];
sum[i][0] = max(sum[i][0], pre[i][j][k]);
}
for(int i = 1; i <= m; i++) sum[i][0] = max(sum[i][0], sum[i-1][0]);
for(int i = m; i; i--)
for(int j = 1; j <= n; j++)
for(int k = j; k <= n; k++){
suf[i][j][k] = max(suf[i+1][j][k], zero)+col[i][k]-col[i][j-1];
sum[i][1] = max(sum[i][1], suf[i][j][k]);
}
for(int i = 1; i < m; i++)
ans = max(ans,sum[i][0]*sum[i+1][1]);
// NO.2
memset(suf, 0, sizeof suf);
memset(pre, 0, sizeof pre);
memset(sum, 0x3f, sizeof sum);
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
for(int k = j; k <= n; k++){
pre[i][j][k] = min(pre[i-1][j][k], zero)+col[i][k]-col[i][j-1];
sum[i][0] = min(sum[i][0], pre[i][j][k]);
}
for(int i = m; i; i--)
for(int j = 1; j <= n; j++)
for(int k = j; k <= n; k++){
suf[i][j][k] = min(suf[i+1][j][k], zero)+col[i][k]-col[i][j-1];
sum[i][1] = min(sum[i][1], suf[i][j][k]);
}
for(int i = 1; i <= m; i++) sum[i][0] = min(sum[i][0], sum[i-1][0]);
for(int i = 1; i < m; i++)
ans = max(ans, sum[i][0]*sum[i+1][1]);
// NO.3
memset(col, 0, sizeof col);
memset(suf, 0, sizeof suf);
memset(pre, 0, sizeof pre);
memset(sum, -0x3f, sizeof sum);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
col[i][j] = col[i][j-1]+a[i][j];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
for(int k = j; k <= m; k++){
pre[i][j][k] = max(pre[i-1][j][k], zero)+col[i][k]-col[i][j-1];
sum[i][0] = max(sum[i][0], pre[i][j][k]);
}
for(int i = n; i; i--)
for(int j = 1; j <= m; j++)
for(int k = j; k <= m; k++){
suf[i][j][k] = max(suf[i+1][j][k], zero)+col[i][k]-col[i][j-1];
sum[i][1] = max(sum[i][1], suf[i][j][k]);
}
for(int i = 1; i <= n; i++) sum[i][0] = max(sum[i][0], sum[i-1][0]);
for(int i = 1; i < n; i++)
ans = max(ans, sum[i][0]*sum[i+1][1]);
// NO.4
memset(suf, 0, sizeof suf);
memset(pre, 0, sizeof pre);
memset(sum, 0x3f, sizeof sum);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
for(int k = j; k <= m; k++){
pre[i][j][k] = min(pre[i-1][j][k], zero)+col[i][k]-col[i][j-1];
sum[i][0] = min(sum[i][0], pre[i][j][k]);
}
for(int i = 1; i <= n; i++) sum[i][0] = min(sum[i][0], sum[i-1][0]);
for(int i = n; i; i--)
for(int j = 1; j <= m; j++)
for(int k = j; k <= m; k++){
suf[i][j][k] = min(suf[i+1][j][k], zero)+col[i][k]-col[i][j-1];
sum[i][1] = min(sum[i][1], suf[i][j][k]);
}
for(int i = 1; i < n; i++)
ans = max(ans, sum[i][0]*sum[i+1][1]);
printf("%lld", ans);
return 0;
}