题目大意:
给出一个n?m的01矩阵,0表示不能放,1表示能放,在其中放入三个矩形,要求满足如下条件:
1.每个矩形面积大于0。
2.这些矩形必须是一个联通块,矩形之间不能重叠。
3.矩形的左边界在同一条线上。
4.中间矩形的横向长度小于两边矩形的横向长度。
求出最大的三个矩形的总面积,无解输出?1。
分析:
我是个蒟蒻,TAT,想了一个晚上+中午+n个课间才想出来怎么做…
首先我们枚举左边界j,O(n)。
然后我们枚举中间矩形的上下边界p,q,O(n2)。
现在已经O(n3)了,然而解法也是O(n3)的(有没有感觉很神奇...)。
首先我们得有一些必要的预处理:rmi,j表示点(i,j)向右能到达的最远距离,minrj,p,q表示第j列以p→q行为左边界的矩形向右能到达的最远距离。
设上矩形的上边界为x,下矩形的下边界为y。
思考一下发现我们要考虑两种情况,一个是中间矩形的minrj,p,q≥minrj,x,p?1且minrj,p,q≥minrj,q+1,y,另一个是<。
先考虑第二种情况,这种情况下我们不需要缩减中间矩形的横向长度。(另一种情况会被上下矩形压制得横向长度减小)
我们先确定中间矩形的上边界p,随着下边界q的下移,minrj,p,q会呈非递增增长,下面矩形的minrj,q+1,y会呈非递减增长,这时我们下面矩形的下边界y的范围可以下移,此时我们要求的就是这个范围内的下面矩形的最大面积值,然而这个是可以预处理的,就是预处理maxdj,q+1,y表示以第j列为左边界且以q+1为上边界,下边界在[q+1,y]范围内的矩形面积最大值,则maxdj,q+1,y=max(maxdj,q+1,y,(y?q)minrj,q+1,y?1),这个预处理是O(n3)的,不虚啊,同样我们可以预处理出类似的上面矩形对应的maxuj,q+1,y,这样我们就可以在O(1)的时间内算出面积最大值,这样我们就解决了这个问题了。
回过头来考虑第一种情况,因为我们被迫缩减中间矩形的横向长度,因此在确定x,y,p,q后,把中间矩形的宽度变小(即把p++或q??后),总面积会变大,这个可以自己想一下(很显然的...),由此我们很显然可以把中间矩形的宽度设为1时,肯定是面积最大的(此时的时间要从枚举第j列算起)。
我们枚举中间矩形的那一行p,再枚举这个矩形缩减后的长度为t,预处理上面矩形的长度大于t的最大矩形面积maxu2j,p?1,t,下面矩形的长度大于t的最大矩形面积maxd2j,p+1,t,这个预处理也是O(n3)的,不虚啊。
总之就差不多了,头一次写这么多,也不知道讲没讲清…能不能看懂看造化了…
AC code:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 189;
int n, m;
int g[MAXN][MAXN];
int rm[MAXN][MAXN];
int minr[MAXN][MAXN];
struct data
{
int c, p1, p2, p3, p4, l1, l2, l3, s;
data(int c=0, int p1=0, int p2=0, int p3=0, int p4=0, int l1=0, int l2=0, int l3=0, int s=0):c(c),p1(p1),p2(p2),p3(p3),p4(p4),l1(l1),l2(l2),l3(l3),s(s){}
}ans;
struct data2
{
int p, l, s;
data2(int p=0, int l=0, int s=0):p(p),l(l),s(s){}
void init() {p = l = s = 0;}
};
data2 maxu[MAXN][MAXN];
data2 maxd[MAXN][MAXN];
data2 maxu2[MAXN][MAXN];
data2 maxd2[MAXN][MAXN];
void work_rm()
{
for(int j = m; j >= 1; --j)
for(int i = 1; i <= n; ++i)
if(!g[i][j])
rm[i][j] = rm[i][j+1]+1;
}
void work_minr(int j)
{
for(int i = 1; i <= n; ++i)
{
minr[i][i] = rm[i][j];
for(int k = i+1; k <= n; ++k)
minr[i][k] = min(minr[i][k-1], rm[k][j]);
}
}
void work_maxu()
{
for(int i = 1; i <= n; ++i)
{
maxu[i][i] = data2(i, minr[i][i], minr[i][i]);
for(int k = i-1; k >= 1; --k)
if(maxu[i][k+1].s > (i-k+1)*minr[k][i])
maxu[i][k] = maxu[i][k+1];
else maxu[i][k] = data2(k, minr[k][i], (i-k+1)*minr[k][i]);
}
}
void work_maxd()
{
for(int i = n; i >= 1; --i)
{
maxd[i][i] = data2(i, minr[i][i], minr[i][i]);
for(int k = i+1; k <= n; ++k)
if(maxd[i][k-1].s > (k-i+1)*minr[i][k])
maxd[i][k] = maxd[i][k-1];
else maxd[i][k] = data2(k, minr[i][k], (k-i+1)*minr[i][k]);
}
}
void work_maxu2()
{
for(int i = 1; i <= n; ++i)
{
int u = i;
for(int j = m; j >= 1; --j)
{
maxu2[i][j].init();
u = min(i, u);
while(u && minr[u][i] > j) u--;
u++;if(u > i) continue;
maxu2[i][j] = maxu[i][u];
}
}
}
void work_maxd2()
{
for(int i = n; i >= 1; --i)
{
int d = i;
for(int j = m; j >= 1; --j)
{
maxd2[i][j].init();
d = max(i, d);
while(d <= n && minr[i][d] > j) d++;
d--;if(d < i) continue;
maxd2[i][j] = maxd[i][d];
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
scanf("%d", &g[i][j]);
work_rm();
for(int j = 1; j <= m; ++j)
{
work_minr(j), work_maxu(), work_maxd();
for(int p = 2; p <= n-1; ++p)
{
int u = p-1, d = p+1;
for(int q = p; q <= n-1 && minr[p][q]; ++q)
{
u = min(p-1, u), d = max(q+1, d);
while(u && minr[u][p-1] > minr[p][q]) u--;u++;if(u >= p) continue;
while(d <= n && minr[q+1][d] > minr[p][q]) d++;d--;if(d <= q) continue;
if(ans.s < (q-p+1)*minr[p][q]+maxu[p-1][u].s+maxd[q+1][d].s)
ans = data(j, maxu[p-1][u].p, p, q+1, maxd[q+1][d].p, maxu[p-1][u].l, minr[p][q], maxd[q+1][d].l, (q-p+1)*minr[p][q]+maxu[p-1][u].s+maxd[q+1][d].s);
}
}
work_maxu2(), work_maxd2();
for(int p = 2; p <= n-1; ++p)
if(minr[p][p] > 1)
for(int q = 1; q < minr[p][p]; ++q)
if(maxu2[p-1][q].s && maxd2[p+1][q].s && ans.s < q+maxu2[p-1][q].s+maxd2[p+1][q].s)
ans = data(j, maxu2[p-1][q].p, p, p+1, maxd2[p+1][q].p, maxu2[p-1][q].l, q, maxd2[p+1][q].l, q+maxu2[p-1][q].s+maxd2[p+1][q].s);
}
for(int i = ans.p1; i < ans.p2; ++i)
for(int j = 1; j <= ans.l1; ++j)
g[i][ans.c+j-1] = 8;
for(int i = ans.p2; i < ans.p3; ++i)
for(int j = 1; j <= ans.l2; ++j)
g[i][ans.c+j-1] = 8;
for(int i = ans.p3; i <= ans.p4; ++i)
for(int j = 1; j <= ans.l3; ++j)
g[i][ans.c+j-1] = 8;
if(ans.s)
{
printf("%d\n", ans.s);
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
printf("%d", g[i][j]);
if(j < m) printf(" ");
}
puts("");
}
}
else puts("-1");
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
#endif
return 0;
}