矩形牛棚(就是最大长方形,但我在LGOJ上没找到)(1114)
题目就是找一个最大长方形。。。。。。
在找最大长方形之前,我们先来回顾一下最大正方形:
题目大意:
在一块地板上整齐的铺满地砖,但其中有一些有污迹,现在要求你找出一个没有污迹的最大正方形(以下1代表有污迹)。
0 1 1 1 0
1 0 0 1 1
0 1 1 0 1
1 1 1 1 0
0 1 1 1 0
其中最大正方形的边长为2
状态转移方程:dp[i][j]=min(dp[i-1][j],min(dp[i-1][j-1],dp[i][j-1]))+1
先给大家推一遍
1 | 1 | 0 |
0 | 1 | 1 |
1 | 1 | 1 |
从第一行开始,我们得到的dp应该是:
1 | 1 | 0 |
0 | 1 | 1 |
1 | 1 | 2 |
以dp为这个正方形的右下角,TA最左边应该到dp[i][j-1]的最左边+1,TA的最上边应该到dp[i-1][j]的最上边+1,TA的左上角应该是dp[i-1][j-1]的左上角+1,综合来看,就可以得到我们的状态转移方程。
完整代码如下:
#include<cstdio>
#include<iostream>
using namespace std;
int dp[1005][1005],i,n,j,k,m,x,y,ans;
bool a[1005][1005];
inline void read(int &x) {
x=0;int f=1;char s=getchar();
while(s<'0'||s>'9')s=getchar();
while(s>='0'&&s<='9')x=x*10+s-48,s=getchar();
x*=f;
}
inline void pr(int x) {
if(x>9)pr(x/10);
putchar(x%10+48);
}//快读快输不解释
int main() {
read(n),read(m);
for(i=1;i<=m;i++)
read(x),read(y),a[x][y]=1;//标记被污染
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(!a[i][j])
dp[i][j]=min(dp[i-1][j],min(dp[i-1][j-1],dp[i][j-1]))+1,ans=max(ans,dp[i][j]);//计算dp的同时更新ans
pr(ans);
}
这是最大正方形,但最大长方形看起来就比较难了,首先数据更大,思路也更难想
同样,再讲最大长方形之前,我们再来看一道题,也是求最大长方形,但是是一维的:
题目大意:
输入一行建筑的高度,其间挂一块广告牌,要求广告牌后面的每一寸地方都要有楼房,且要求广告牌的面积最大。
大致想法就是以每一栋建筑作为最矮的建筑的最大面积
是不是有点拗口?我们接着看
我们用一个栈,依次push进去每一栋建筑的高度,然后可以把TA大致分为3种情况:
(当前建筑记作r,高度记作h,栈记作s)
1、s.top().h>r.h;
2、s.top().h=r.h;
3、s.top().h<r.h;
针对这三种情况,我们可以得到:
1、重复取出s里面的元素,直到满足3;
2、不做处理
3、s.push(r);
为什么呢,我在代码里给大家解释:
#include<cstdio>
#include<stack>
#include<iostream>
using namespace std;
inline void read(int &x) {
x=0;
int f=1;
char s=getchar();
while(s<'0'||s>'9') {
if(s=='-')
f=-1;
s=getchar();
}
while(s>='0'&&s<='9') {
x=x*10+s-48;
s=getchar();
}
x*=f;
}
inline void pr(int x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>9)
pr(x/10);
putchar(x%10+48);
}//快读快输不解释
struct node {
int id,h;
}r,o;
int dp[100005],n,ans;
inline int getans(int f[]) {
int maxn=0;
stack<node>s;//栈用来储存建筑
f[n+1]=0;//因为要把每一栋建筑都计算一遍,才能得到答案,如果没有这一步把栈里的元素都pop出去,
//就不能完整的计算出所有矩形的面积
for(int i=1;i<n+2;i++) {
r.h=f[i];
r.id=i;
if(s.empty()||r.h>s.top().h)//当TA是空的时候或者当前建筑的高度比栈顶的建筑高,说明
//这座建筑的右边还有可以发展的空间,广告牌的面积还可以更大
s.push(r);//直接push
else if(r.h<s.top().h) {//如果TA的高比栈顶的还要矮,说明以栈顶为最矮的建筑左右两边都
//到底了
while(!s.empty()&&r.h<s.top().h) {//就用一个while计算已经到底的建筑的面积
o=s.top();
s.pop();
maxn=max(maxn,(i-o.id)*o.h);//更新最大值
r.id=o.id;//这里更新之后就可以得出该建筑向左边的最长的距离
}
s.push(r);
}
}
return maxn;
}
int main() {
read(n);
for(int i=1;i<=n;i++)
read(dp[i]);//读入部分
ans=max(ans,getans(dp));//计算答案
pr(ans);
}
如果这个懂了,接下来的题就很简单了,我们只需要把这道在瓷砖上的题转换成每一行的直方图就行了。
献上代码:
#include<cstdio>
#include<stack>
#include<iostream>
using namespace std;
inline void read(int &x) {
x=0;
int f=1;
char s=getchar();
while(s<'0'||s>'9') {
if(s=='-')
f=-1;
s=getchar();
}
while(s>='0'&&s<='9') {
x=x*10+s-48;
s=getchar();
}
x*=f;
}
inline void pr(int x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>9)
pr(x/10);
putchar(x%10+48);
}
struct node {
int id,h;
}r,o;
bool a[3005][3005];
int dp[3005][3005],n,k,m,x,y,ans;
inline int getans(int f[]) {
int maxn=0;
stack<node>s;
f[m+1]=0;
for(int i=1;i<m+2;i++) {
r.h=f[i];
r.id=i;
if(s.empty()||r.h>s.top().h)
s.push(r);
else if(r.h<s.top().h) {
while(!s.empty()&&r.h<s.top().h) {
o=s.top();
s.pop();
maxn=max(maxn,(i-o.id)*o.h);
r.id=o.id;
}
s.push(r);
}
}
return maxn;
}
int main() {
read(n),read(m),read(k);
for(int i=1;i<=k;i++)
read(x),read(y),a[x][y]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!a[i][j])
dp[i][j]=dp[i-1][j]+1;//预处理计算“建筑的高度”
for(int i=1;i<=n;i++)
ans=max(ans,getans(dp[i]));//每一行都要计算
pr(ans);
}
大概就是这样,不懂的可以一起讨论讨论.