矩形牛棚題解
阿新 • • 發佈:2018-12-27
矩形牛棚(就是最大長方形,但我在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);
}
大概就是這樣,不懂的可以一起討論討論.