修建泳池&最大子矩阵
【题目描述】
夏天到了,学校打算在教学楼后面的空地上挖一个泳池供大家使用。
经过实地勘察,这块土地可以划分成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个重要的数组:
我们要定义L数组,大小是n*m的。l[i][j]表示第i行第j列的矩阵元素所在的最大矩阵中,第i行第j列的矩阵元素左边(包括它自己)有多少个连续的0 。
我们还要定义一个R数组,大小和L数组一样。r[i][j]表示第i行第j列的矩阵元素所在的矩阵中,第i行第j列的矩阵元素右边(包括它自己)有多少个连续的0 。
有了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中的每一个22的矩形酷不酷即可。
然而这样还是会时超。。。
我们可以看一下判断的条件:
\]
移项,变成:
\]
也就是
\]
由于我们只用判断2*2的矩形,因此(设(i,j)是该矩形的左上角)
\]
可以发现括号内的值没什么关系,可以预处理。所以我们可以新建一个矩阵B,其中
\]
再新建一个C数组,其中
\]
然后再新建一个数组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;
}
修建泳池&最大子矩阵的更多相关文章
- 防线修建 bzoj 2300
防线修建(1s 512MB)defense [问题描述] 近来A国和B国的矛盾激化,为了预防不测,A国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了.可是A国上层现在还 ...
- ACM 中 矩阵数据的预处理 && 求子矩阵元素和问题
我们考虑一个$N\times M$的矩阵数据,若要对矩阵中的部分数据进行读取,比如求某个$a\times b$的子矩阵的元素和,通常我们可以想到$O(ab)$的遍历那个子矩阵,对它的各 ...
- [BZOJ1127][POI2008] KUP子矩阵
Description 给一个n*n的地图,每个格子有一个价格,找一个矩形区域,使其价格总和位于[k,2k] Input 输入k n(n<2000)和一个n*n的地图 Output 输出矩形的左 ...
- 【SCOI2005】 最大子矩阵 BZOJ 1084
Description 这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大.注意:选出的k个子矩阵不能相互重叠. Input 第一行为n,m,k(1≤n≤100,1≤m≤2 ...
- 一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵
题目描述: 一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵(矩阵中元素个数为矩阵面积) 输入: 每个案例第一行三个正整数N,M<=100,表示矩阵大小,和一个整数K 接下 ...
- HDU1559 最大子矩阵 (二维树状数组)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1559 最大子矩阵 Time Limit: 30000/10000 MS (Java/Others) ...
- bzoj1057: [ZJOI2007]棋盘制作--最大子矩阵
既然要求最大01子矩阵,那么把应该为0的位置上的数取反,这样就变成求最大子矩阵 最大子矩阵可以用单调栈 #include<stdio.h> #include<string.h> ...
- hdu 1559 最大子矩阵
最大子矩阵 Time Limit: 30000/10000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Sub ...
- NOIP2014pj子矩阵[搜索|DP]
题目描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵. 例如,下面左图中选取第2.4行和第2.4.5列交叉位置的元素 ...
随机推荐
- 论文阅读:OpenFlow: Enabling Innovation in Campus Networks
摘要: 本白皮书提出了OpenFlow——研究人员在他们每天使用的网络中运行实验协议的一种方式. OpenFlow基于以太网交换机,具有内部流表以及用于添加和删除流条目的标准化接口.我们的目标是鼓励网 ...
- Unity3D_(数据)LitJson创建和解析Json
LitJson github: 传送门 JsonUtility创建和解析Json 传送门 LitJson.dll百度云盘 传送门 密码:p1py 加载LitJson.dll到Unity中 在Asset ...
- perl基础-2
函数参数 perl 函数参数为$$,$$$,$@ Perl 可以通过函数元型在编译期进行有限的参数类型检验. 如果你声明 sub mypush (+@)那么 mypush() 对参数的处理就同内置的 ...
- 2019java第十二周课程总结
本周主要还是学习图形界面 各种容器使用方法 如下代码: package text10; import java.awt.*; import java.io.File; import javax.swi ...
- ubuntu16.04修改host上外網
1.打开hosts文件: sudo emacs /etc/hosts 2.加入下面的内容 #chrome同步服务器 203.208.46.132 chrome.google.com203.208.46 ...
- koa 基础(一)新建/启动 项目
1.创建项目 npm init -y npm install --save koa 2.app.js // 引入模板 var koa = require('koa'); // 实例化 var app ...
- 运算 Kotlin(3)
运算Kotlin支持数字运算的标准集,运算被定义为相应的类成员(但编译器会将函数调用优化为相应的指令) . 参见运算符重载.对于位运算,没有特殊字符来表示,而只可用中缀方式调用命名函数,例如:val ...
- 第一部分 Python基础知识
Python测试开发核心编程主要分两部分,python3基础和Python进阶,每部分的重点内容如下 一. Python测试开发核心编程 数据类型 控制结构 异常处理 文件操作 线程与进程(了解) 常 ...
- Python中的super()用法
Python中对象方法的定义很怪异,第一个参数一般都命名为self(相当于其它语言的this,比如:C#),用于传递对象本身,而在调用的时候则不 必显式传递,系统会自动传递. 今天我们介绍的主角是su ...
- django中的model 的unique_together(联合唯一)
unique_together解释 nique_together 这个元数据是非常重要的一个!它等同于数据库的联合约束! 举个例子,假设有一张用户表,保存有用户的姓名.出生日期.性别和籍贯等等信息.要 ...