1. 程式人生 > >矩形牛棚題解

矩形牛棚題解

矩形牛棚(就是最大長方形,但我在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);
}

大概就是這樣,不懂的可以一起討論討論.