【题目描述】

夏天到了,学校打算在教学楼后面的空地上挖一个泳池供大家使用。

经过实地勘察,这块土地可以划分成N 行M 列的方格,有的方格是树,有的方格是空地。现在要找一块最大的矩形空地修建泳池,请问泳池的面积有多大?

【输入】

第一行两个正整数N, M,分别表示土地的行数和列数。

接下来N行,每行有M个用空格隔开的数a[i][j],每个数均为0或1,0表示空地,1表示树。

【输出】

输出一行一个整数,表示最大的泳池面积。

【样例输入】

样例输入1

4 5

0 1 0 1 0

0 0 0 0 0

0 0 0 0 1

1 0 0 0 0

样例输入2

4 5

1 1 0 1 1

1 1 0 0 1

1 0 0 0 0

0 0 0 0 0

【样例输出】

样例输出1

9

样例输出2

8

【数据范围限制】

对于10%的数据,所有的0构成一个纯天然的长方形区域

对于30%的数据,所有的0构成若干个互不相交的有树隔开的长方形区域

对于另外30%的数据,所有的0构成一个柱状图,即每一列所有的1都在该列的最上方

对于100%的数据,1<=N, M<=2000

【提示】

样例解释1

泳池的左上角为(2,2),右下角为(4,4),面积为9。

样例解释2

所有的0构成一个柱状图,即每一列所有的1都在该列的最上方,泳池的左上角为(3,2),右下角为(4,5),面积为8。这种情况泳池的右下角一定在最后一行!

【题解】

这题还是蛮WS的!题目其实就是让我们求出由0构成的最大矩阵面积。

这题真不愧是“水分容易AC难”。比赛时,我就打了一个暴力程序,枚举矩阵的左上角和右下角,再用个前缀和维护一下,然后便水到了60分。

其实这题我们可以用一种O(nm)的方法来做。

首先,我们可以定义3个重要的数组:

  1. 我们要定义L数组,大小是n*m的。l[i][j]表示第i行第j列的矩阵元素所在的最大矩阵中,第i行第j列的矩阵元素左边(包括它自己)有多少个连续的0 。

  2. 我们还要定义一个R数组,大小和L数组一样。r[i][j]表示第i行第j列的矩阵元素所在的矩阵中,第i行第j列的矩阵元素右边(包括它自己)有多少个连续的0 。

  3. 有了L数组和R数组,我们还要定义一个什么数组呢?当然是up数组啦!up[i][j]表示第i行第j列所在的最大矩阵中,第i行第j列的矩阵元素上面(包括它自己)有多少个连续的0 。

但是,光是定义还不够,我们还必须了解它们怎么用:

  • 我们可以用这三个数组来更新答案:

      - $ans=max(ans,(l[i][j]+r[i][j]-1) * up[i][j]);$
  • 我们怎么求出它们的值呢?我们必须知道:

第i行第j列的矩阵元素所在的最大矩阵的列的长度一定是等于第i行第j列的矩阵元素的上方最小的列的长度(也就是说,$l_{i,j}= \min(l_{i-k,j}) (k<up_{i,j}),r_{i,j}=\min(r_{i-k,j}) (k<up_{i,j}) $)

千万别担心这样求出来的答案不会是最小值。比如说下面这种情况:(红色表示1,蓝色表示0)

-

 - 在这种情况中:虽然在第4行第3列这一个格子中,所求出的矩阵只是3*1大小的(答案明显是最后一行的那个矩阵),但是,第4行的其它格子求出来的正是这个矩阵呀!绝对会有一些格子求出你想要的答案的。

在打这道题的代码时,思路一定要清晰,不然就会出错哟~

#include<cstdio>
#include<cstring>
using namespace std;
int a[2010][2010],up[2010][2010],l[2010][2010],r[2010][2010];
int mymin(int x,int y)
{
if(x<y) return x;
else return y;
}
int main()
{
int n,m,i,j,k,t,ans=0;
scanf("%d%d",&n,&m);
memset(a,1,sizeof(a));
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
if(a[i][j]==0)
{
if(a[i][j-1]==0) l[i][j]=l[i][j-1]+1;
else l[i][j]=1;
}
}
}
for(i=1;i<=n;i++)
{
for(j=m;j>0;j--)
{
if(a[i][j]==0)
{
if(a[i][j+1]==0) r[i][j]=r[i][j+1]+1;
else r[i][j]=1;
}
}
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
if(a[i][j]==0)
{
if(a[i-1][j]==0)
{
up[i][j]=up[i-1][j]+1;
if(a[i-1][j]==0)
{
l[i][j]=mymin(l[i][j],l[i-1][j]);
r[i][j]=mymin(r[i][j],r[i-1][j]);
}
}
else
{
up[i][j]=1;
}
}
}
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
t=(r[i][j]+l[i][j]-1)*up[i][j];
if(ans<t) ans=t;
}
}
printf("%d\n",ans);
return 0;
}

加强版

【NOIP2015模拟10.22】最大子矩阵

题目

【题目描述】

我们将矩阵A中位于第i行第j列的元素记作A[i,j]。一个矩阵A是酷的仅当它满足下面的条件:

\(A[1,1]+A[r,s]<=A[1,s]+A[r,1](r,s>1)\)

其中r为矩阵A的行数,s为矩阵A的列数。

进一步,如果一个矩阵是非常酷的仅当它的每一个至少包含两行两列子矩阵都是酷的。

你的任务是,求出一个矩阵A中的一个非常酷的子矩阵B,使得B包含最多元素。

【输入】

第一行包含两个整数R,S(2<=R,S<=1000),代表矩阵的行数与列数。

接下来R行每行包括S个整数,代表矩阵中的元素,矩阵中元素的绝对值不大于1000000。

【输出】

一行一个整数,代表子矩阵B的元素总数。如果没有一个非常酷的子矩阵,输出0。

【样例输入】

输入1:

3 3

1 4 10

5 2 6

11 1 3

输入2:

3 3

1 3 1

2 1 2

1 1 1

输入3:

5 6

1 1 4 0 3 3

4 4 9 7 11 13

-3 -1 4 2 8 11

1 5 9 5 9 10

4 8 10 5 8 8

【样例输出】

输出1:

9

输出2:

4

输出3:

15

样例3解释

在第三个样例中,子矩阵B的左上角为A[3,2],右下角为A[5,6]。

【数据范围限制】

对于60%的数据,满足R,S<=350。

对于100%的数据,满足2<=R,S<=1000,矩阵中元素的绝对值不大于1000000。


题解

可以证明得出:

当一个矩阵A中包含的所有的22的矩形都是酷的时,A非常酷。

因此,我们只需要判断A中的每一个2
2的矩形酷不酷即可。

然而这样还是会时超。。。

我们可以看一下判断的条件:

\[A_{1,1}+A_{r,s}\leq A_{1,s}+A_{r,1}(r,s>1)
\]

移项,变成:

\[A_{1,1}-A_{1,s}\leq A_{r,1}-A_{r,s}(r,s>1)
\]

也就是

\[A_{1,1}-A_{1,s}-(A_{r,1}-A_{r,s})\leq 0(r,s>1)
\]

由于我们只用判断2*2的矩形,因此(设(i,j)是该矩形的左上角)

\[(A_{i,j}-A_{i,j+1})-(A_{i+1,j}-A_{i+1,j+1})\leq 0
\]

可以发现括号内的值没什么关系,可以预处理。所以我们可以新建一个矩阵B,其中

\[B_{i,j}=A_{i,j}-A_{i,j+1}
\]

再新建一个C数组,其中

\[\begin{aligned}C_{i,j}&=(A_{i,j}-A_{i,j+1})-(A_{i+1,j}-A_{i+1,j+1})\\&=B_{i,j}-B_{i+1,j}\end{aligned}
\]

然后再新建一个数组D,如果\(C_{i,j}\le 0\),那么\(D_{i,j}=0\)

然后这个问题就被转化为修建泳池了(注意:计算答案时,最大矩阵的行、列都要+1)。


加强版

#include<cstdio>
using namespace std;
int a[1010][1010],up[1010][1010],left[1010][1010],right[1010][1010];
bool b[1010][1010];
int main()
{
freopen("max.in","r",stdin);
freopen("max.out","w",stdout);
int n,m,i,j,k,ans=0;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(i=1;i<=n;i++)
for(j=1;j<m;j++)
a[i][j]-=a[i][j+1];
for(i=1;i<n;i++)
{
for(j=1;j<m;j++)
{
a[i][j]-=a[i+1][j];
if(a[i][j]<=0)
{
b[i][j]=1;
if(b[i][j-1]) left[i][j]=left[i][j-1]+1;
else left[i][j]=1;
}
}
for(j=m-1;j>0;j--)
if(b[i][j])
{
if(b[i][j+1]) right[i][j]=right[i][j+1]+1;
else right[i][j]=1;
}
for(j=1;j<m;j++)
if(b[i][j])
{
if(b[i-1][j])
{
up[i][j]=up[i-1][j]+1;
if(left[i][j]>left[i-1][j]) left[i][j]=left[i-1][j];
if(right[i][j]>right[i-1][j]) right[i][j]=right[i-1][j];
}
else up[i][j]=1;
k=(left[i][j]+right[i][j])*(up[i][j]+1);
if(k>ans) ans=k;
}
}
printf("%d\n",ans);
return 0;
}

修建泳池&最大子矩阵的更多相关文章

  1. 防线修建 bzoj 2300

    防线修建(1s 512MB)defense [问题描述] 近来A国和B国的矛盾激化,为了预防不测,A国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了.可是A国上层现在还 ...

  2. ACM 中 矩阵数据的预处理 && 求子矩阵元素和问题

            我们考虑一个$N\times M$的矩阵数据,若要对矩阵中的部分数据进行读取,比如求某个$a\times b$的子矩阵的元素和,通常我们可以想到$O(ab)$的遍历那个子矩阵,对它的各 ...

  3. [BZOJ1127][POI2008] KUP子矩阵

    Description 给一个n*n的地图,每个格子有一个价格,找一个矩形区域,使其价格总和位于[k,2k] Input 输入k n(n<2000)和一个n*n的地图 Output 输出矩形的左 ...

  4. 【SCOI2005】 最大子矩阵 BZOJ 1084

    Description 这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大.注意:选出的k个子矩阵不能相互重叠. Input 第一行为n,m,k(1≤n≤100,1≤m≤2 ...

  5. 一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵

    题目描述: 一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵(矩阵中元素个数为矩阵面积) 输入: 每个案例第一行三个正整数N,M<=100,表示矩阵大小,和一个整数K 接下 ...

  6. HDU1559 最大子矩阵 (二维树状数组)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1559 最大子矩阵 Time Limit: 30000/10000 MS (Java/Others)  ...

  7. bzoj1057: [ZJOI2007]棋盘制作--最大子矩阵

    既然要求最大01子矩阵,那么把应该为0的位置上的数取反,这样就变成求最大子矩阵 最大子矩阵可以用单调栈 #include<stdio.h> #include<string.h> ...

  8. hdu 1559 最大子矩阵

    最大子矩阵 Time Limit: 30000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  9. NOIP2014pj子矩阵[搜索|DP]

    题目描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵. 例如,下面左图中选取第2.4行和第2.4.5列交叉位置的元素 ...

随机推荐

  1. CF603E Pastoral Oddities 优先队列+结论+LCT维护生成树

    首先,一个神奇的结论:一个合法的方案存在的条件是每一个联通块的节点数都是偶数个的. 这个可以用数学归纳法简单证一证. 证出这个后,我们只需动态加入每一个边,并查看一下有哪些边能够被删除(删掉后联通块依 ...

  2. ubuntu下jps命令无效

    jps命令无效 #启动zookeeper $ sudo ./zkServer.sh start [sudo] jjboom 的密码: ZooKeeper JMX enabled by default ...

  3. python 手动拼接json数据

    第一步:分别拼接为字符串 第二步:将字符串转化为list 第三歩:将两个list合并为dict 第四步:将dict转换为接送数据 如:  import json keys = ['a', 'b', ' ...

  4. HDU 3480 Division DP斜率优化

    解题思路 第一步显然是将原数组排序嘛--然后分成一些不相交的子集,这样显然最小.重点是怎么分. 首先,我们写出一个最暴力的\(DP\): 我们令$F[ i ][ j ] $ 为到第\(i\)位,分成\ ...

  5. CodeForces 788B--Weird journey

    Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Description Little ...

  6. Unity3D_(API)Quaternion四元数中的Quaternion.LookRotation()

    四元数百度百科: 传送门 四元数官方文档: 传送门 欧拉旋转.四元数.矩阵旋转之间的差异: 传送门 四元数转换为欧拉角eulerAngles 官方文档: 传送门 欧拉角转换为四元数Euler 官方文档 ...

  7. extentsreport testng美化报告生成

    一:主要内容 优化testng测试报告,使用extentsreport 解决extentsreport打开后加载不出来样式的问题 二:报告效果 先上图,看下testng extentsreport报告 ...

  8. vuex 的介绍

    vue-cli 中 css 的作用域 scoped vue 数据的为响应数据,一改全改,一变全变的特性,我们的很多处理也会围绕着他 vuex 是处理数据的,是 vue 的数据仓库 vuex 的作用:采 ...

  9. webpack 第二部分

    默认根目录 当前项目 修改目录 devServer devServer:{ open:true, //自动打开浏览器 port:3000, // 端口 contentBase:"dist&q ...

  10. Dubbo HelloWord 与 Spring Boot 整合

    实现消费者项目代码调用提供者项目代码,使用 zookeeper 做为注册中心 interface 项目 pom.xml <?xml version="1.0" encodin ...