【题目描述】

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

经过实地勘察,这块土地可以划分成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. 总结JavaScript中浏览器的兼容问题

    浅析JavaScript中浏览器的兼容问题 浏览器兼容性问题是在实际开发中容易忽略而又最重要的一部分.我们在讲老版本浏览器兼容问题之前,首先要了解什么是能力检测,它是来检测浏览器有没有这种能力,即判断 ...

  2. 手动升级 Confluence 6 - 升级问题解决

    升级失败了? 如果你需要对你的升级进行回退,你必须首先恢复你老的 Confluence 备份.不要尝试再次进行升级,也不要尝试再次对升级失败的 Confluence 进行再次启动.  在升级过程中遇到 ...

  3. 10个PHP代码片段

    还记得CSDN研发频道此前发表过的一篇<可以直接拿来用的15个jQuery代码片段>吗?本文笔者将继续为你奉上10个超级有用的PHP代码片段. PHP是一种HTML内嵌式的语言,是一种在服 ...

  4. JavaWeb-SpringBoot_使用H2数据库实现用户注册登录

    使用Gradle编译项目 传送门 前端资源同:使用MySQL数据库实现用户管理_demo 传送门 H2:SpringBoot内置持久化数据库  使用H2数据库实现用户注册登录 用户可以在index.h ...

  5. numpy小记

    import numpy as np # a=np.array([[1,3,2],[4,5,6]]) print(a) a=np.arange(1,13).reshape((3,4))#生成一个3行4 ...

  6. C# 查看计算机端口使用状态

    using System.Net.NetworkInformation; /// <summary> /// 获取第一个可用的端口号 /// </summary> /// &l ...

  7. .Net 无法打开Offie Open XML文件(上传和下载使用)

    代码: 首先,贴一个上传 public static string UploadFileIntoDir(FileUpload MyFile, string DirName) { if (IfOkFil ...

  8. Django测试环境环境配置

    安装Django 在cmd的管理者模式下进入Python的安装目录 敲入命令:pip install Django -看到如下的信息表示成功 如果你在运行其它人写的Django系统时出现以下错误信息, ...

  9. WPF图标拾取器

    <Grid x:Name="LayoutRoot"> <Border BorderBrush="> <Border.Effect> & ...

  10. redis的LRU策略理解

    首先看下serverCron中,服务器每次循环执行的时候,都会刷新server.lrulock. int serverCron(struct aeEventLoop *eventLoop, long ...