PROBLEM2.石材切割
问题描述:
某人得到一块N*M个小格的矩形石材(可能是玉石),经专家分析,把这个矩形石材的每个小格都有一个价值(使用一个绝对值不大于10的整数来描述),现在将这块石材切割成两块矩形石材,注意,切割只能与该矩形边平行,也就是说不能把矩形的小格切碎,假设每块矩形石材的价值为该矩形中所有小格子价值之和。
问怎样切割,才能使得这两个矩形的价值乘积最大。如下图是一种比较好的切割方式。
输入格式:
输入文件BRICK.IN的第一行为2个正整数N和M,表示石材被划分为N*M个格子。接下来N行,每行有M个整数,代表这个格子的价值。
输出格式:
输出文件BRICK.OUT只有一行,包含一个整数,为两个矩形的价值的最大乘积。
输入样例 | 输出样例 |
34 -1-1 -1 -1 00 0 0 -1 -1-1 -1 | 16 |
数据范围
对于30%的数据,满足N,M≤5。
对于100%的数据,满足N,M≤100。每个小格的伤害值的绝对值不超过10。
一切数据及中间变量不超过longint范围。
此题调得甚为蛋疼。。
最开始的时候没有读懂题,以为就切一刀就行了,然后就分成了两个矩形,原来切的数量任意。
当时是WA50,人品也还是不错了。
这是一道最大子矩形和+枚举的题。说的是O(n^3),可是我怎么看怎么是O(n^4)。
先讲最大子矩形和。就是压缩的方法,枚举上下界或者左右界,对每一个上下界或左右界求一个最大子区间和。
这里要用到二维前缀和。(这里一开始一直没弄清楚,应该是第j列的前i行、第i行的前j列,而不是前i行前j列)
预处理出来之后,就可以用O(n^4)来枚举两个上下界,或两个左右界,求出最优解。
本题要注意的就是有可能最优解是两个最小的负数相乘得来。所以总共进行四次DP。出解
AC程序
//#include <iostream>
//using std::cout;
//using std::cin;
#include <cstdio>
const long oo = 0x7fff0000;
long n;long m;
long num[110][110];
long sum[110][110];
long sum2[110][110];
long f[110][110];
long ans = -oo;
int main()
{
freopen("brick.in","r",stdin);
freopen("brick.out","w",stdout);
scanf("%ld%ld",&n,&m);
for (long i=1;i<n+1;i++)
{
for (long j=1;j<m+1;j++)
{
scanf("%ld",&num[i][j]);
sum[i][j] = sum[i-1][j]+num[i][j];
sum2[i][j] = sum2[i][j-1]+num[i][j];
}
}
long max = -oo;
for (long i=1;i<n+1;i++)
{
for (long j=i;j<n+1;j++)
{
f[i][j] = -oo;
long x = sum[j][1]-sum[i-1][1];
f[i][j] = x;
max = x;
for (long k=2;k<m+1;k++)
{
x = sum[j][k]-sum[i-1][k];
if (max>0) max+=x;
else max = x;
if (f[i][j] < max)
f[i][j] = max;
}
}
}
for (long i=1;i<n+1;i++)
{
for (long j=i;j<n+1;j++)
{
for (long k=j+1;k<n+1;k++)
{
for (long l=k;l<n+1;l++)
{
ans >?= f[i][j]*f[k][l];
}
}
}
}
for (long i=1;i<m+1;i++)
{
for (long j=i;j<m+1;j++)
{
f[i][j] = -oo;
long x = sum2[1][j]-sum2[1][i-1];
f[i][j] = x;
max = x;
for (long k=2;k<n+1;k++)
{
x = sum2[k][j]-sum2[k][i-1];
if (max>0) max+=x;
else max = x;
if (f[i][j] < max)
f[i][j] = max;
}
}
}
for (long i=1;i<m+1;i++)
{
for (long j=i;j<m+1;j++)
{
for (long k=j+1;k<m+1;k++)
{
for (long l=k;l<m+1;l++)
{
ans >?= f[i][j]*f[k][l];
}
}
}
}
long min = oo;
for (long i=1;i<n+1;i++)
{
for (long j=i;j<n+1;j++)
{
f[i][j] = oo;
long x = sum[j][1]-sum[i-1][1];
f[i][j] = x;
min = x;
for (long k=2;k<m+1;k++)
{
x = sum[j][k]-sum[i-1][k];
if (min<0) min+=x;
else min = x;
if (f[i][j] > min)
f[i][j] = min;
}
}
}
for (long i=1;i<n+1;i++)
{
for (long j=i;j<n+1;j++)
{
for (long k=j+1;k<n+1;k++)
{
for (long l=k;l<n+1;l++)
{
ans >?= f[i][j]*f[k][l];
}
}
}
}
for (long i=1;i<m+1;i++)
{
for (long j=i;j<m+1;j++)
{
f[i][j] = oo;
long x = sum2[1][j]-sum2[1][i-1];
f[i][j] = x;
min = x;
for (long k=2;k<n+1;k++)
{
x = sum2[k][j]-sum2[k][i-1];
if (min<0) min+=x;
else min = x;
if (f[i][j] > min)
f[i][j] = min;
}
}
}
for (long i=1;i<m+1;i++)
{
for (long j=i;j<m+1;j++)
{
for (long k=j+1;k<m+1;k++)
{
for (long l=k;l<m+1;l++)
{
ans >?= f[i][j]*f[k][l];
}
}
}
}
printf("%ld",ans);
return 0;
}
50分程序:
//#include <iostream>
//using std::cout;
//using std::cin;
#include <cstdio>
const long oo = 0x7fff0000;
long n;long m;
long s1[110];
long s2[110];
long ss = 0;
long num[110][110];
int main()
{
freopen("brick.in","r",stdin);
freopen("brick.out","w",stdout);
scanf("%ld%ld",&n,&m);
for (long i=1;i<n+1;i++)
{
for (long j=1;j<m+1;j++)
{
scanf("%ld",&num[i][j]);
s1[i] += num[i][j];
s2[j] += num[i][j];
}
ss += s1[i];
s1[i] += s1[i-1];
}
for (long i=2;i<m+1;i++)
{
s2[i] += s2[i-1];
}
long ans = -oo;
for (long i=1;i<m;i++)
{
if (ans<s2[i]*(ss-s2[i]))
ans=s2[i]*(ss-s2[i]);
}
for (long i=1;i<n;i++)
{
if (ans<s1[i]*(ss-s1[i]))
ans=s1[i]*(ss-s1[i]);
}
printf("%ld",ans);
return 0;
}