题目描述
到底是个资本家,Farmer John想通过买更多的奶牛来扩大它的生意。它需要给奶牛建造一个新的牛棚。 FJ买了一个矩形的R(1 <= R <= 3000)行C(1 <= C <= 3000)列的牧场。不幸的是,他发现某些1 x 1的区域被损坏了,所以它不可能在把整个牧场建造成牛棚了。
FJ数了一下,发现有P(1 <= p <= 30000)个1 x 1的损坏区域并且请你帮助他找到不包含损坏区域的面积最大的牛棚。 * (对同一个损坏区域可能有多次描述——aLeN.PN注)
输入
第1行: 三个空格隔开的整数 R, C, and P.
第2..P+1行: 每行包含两个空格隔开的整数, r和c, 给出一个损坏区域的行号和列号.
输出
第1行: 牛棚的最大可能面积
样例输入
3 4 2
1 3
2 1
样例输出
6
解题思路
此题利用单调栈进行求解,题目的意思就是说求一个破损的矩阵中一个最大的子矩阵。
用单调栈,单调栈跟单调队列其实是一个意思,只不过只有一边进出。
我们思考一下如果i为子矩阵中高度最小的矩阵,那么i所能构造的最大子矩阵也就是i左边第一个比它矮到i右边第一个比它矮的距离再乘上i的高度,那么原题便转换成了max(f[i] * h[i])(f[i]表示i左边第一个比它矮到i右边第一个比它矮的距离,h[i]表示i点的高度。)那么最关键的就是求i的l与r了,这时候单调栈(队列)就派上用场了。
首先确定思路,我们维护栈内元素呈递增,那么装进h[i]时,便需要在栈里面弹东西。最后找到适合的放置位置便可以装入栈退出了。于是在这样一种操作中就可以求出f[i]了,模拟一下可以知道,当i进栈时,便找到了l,当i出栈时,便找到了r。
于是这道题就可以解决了。
大体思路其实不难。利用这种方法转换为二维,我们每一排就做一下这样的一种操作就行了。
总结
这道题错得很多,虽然错得也很基础,但还是一步一步调出来了题。首先就是预处理了。一开始我的预处理是会超时的。
超时预处理代码:
scanf ("%d%d%d",&r,&c,&p);
for (int i = 1;i <= r ;i ++ )
for (int j = 1 ; j <= c; j ++ )
sum [i][j] = i;
for (int i = 1;i <= p ;i ++ ){
int a,b;
scanf ("%d%d",&a,&b);
sum [a][b] = 0;
for (int j = a + 1;j <= r ; j ++ )
sum [j][b] = j - a;
}
读者们也一定会发现,有很多算重复了,那么在p<=30000这么大的数据范围,这样进行预处理太麻烦了。我们便应该想到标记和前缀和的思想。如果全部完好,那么我们就会发现sum[i][j] = sum[i - 1][j] + 1,然后再处理损坏情况。就比如说(i,j)是损坏了的。那么sum[i + 1][j] = 1。此时如果sum[i][j] == 0, 那么岂不是就可以按这样的操作处理?
由于sum数组本来全都是0,那么我们先将破损的标记为-1,然后再按平常的操作计算。于是这道题就可以在O(r * c)的时间复杂度下完成,在这一道题是可以过的。当然,这道题亦可不用预处理。但这样确实要好理解一些。
正确预处理操作(吓得我还卡个常):
register int i , j , a , b;
read(r);
read(c);
read(p);
for (i = 1;i <= p ;i ++ ){
read(a);
read(b);
sum [a][b] = -1;
}
for (i = 1;i <= r ;i ++ ){
for (j = 1; j <= c; j ++){
if (sum[i][j] == -1){
sum[i][j] = 0;
continue;
}
sum[i][j] = sum[i-1][j] + 1;
}
}
自信满满地交。WA了。当时心态还算乐观。马上回去调代码,但一直不过。内心绝望。于是去做和此题差不多,但是用一维的一道题。打着打着,诶,居然写错了一个,把结构体里面的id写成了x。当时没怎么在意,交上去,对了。。。
那我这题为啥不对,我再次看这个题的代码。。。突然发现我居然把这道题的id也写成x了。。。
WA: AC:
面对这个现实,我感到好笑,同时也找到了自身所存在的问题,下标所对应的值按理说是与下标差距很大的,而我却没有看出来这个问题,证明对于程序的调试还是没有耐心,不够仔细。NOIP爆炸的我的确应该吸取教训。
最后附上代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<stack>
#define MAXN 3005
using namespace std;
struct node {
int x,id;
node (){};
node(int X,int ID){
x = X;
id = ID;
}
};
stack<node>Q;
int r , c , p , sum[MAXN][MAXN] , bi[MAXN] , e[MAXN] , ans;
inline void read(int &x){
int f = 1;
x = 0;
char s = getchar();
while (s < '0'|| s > '9'){
if (s == '-')
f = -1;
s = getchar ();
}
while (s >= '0' && s <= '9'){
x = x * 10 + s - '0';
s = getchar();
}
x *= f ;
}
int main(){
register int i , j;
read(r);
read(c);
read(p);
for (i = 1;i <= p ;i ++ ){
int a,b;
read(a);
read(b);
sum [a][b] = -1;
}
for (int i = 1;i <= r ;i ++ ){
for (int j = 1; j <= c; j ++){
if (sum[i][j] == -1){
sum[i][j] = 0;
continue;
}
sum[i][j] = sum[i-1][j] + 1;
}
}
for (i = 1;i <= r; i ++ ){
memset(bi,0,sizeof(bi));
memset(e,0,sizeof(e));
while (!Q.empty())
Q.pop();
for (j = 1;j <= c + 1; j ++ ){
while (!Q.empty()){
node X = Q.top();
if (X.x <= sum [i][j]){
bi[j] = X.id + 1;
Q.push(node (sum[i][j],j));
break;
}
else{
Q.pop();
e[X.id] = j - 1;
ans = max (ans,(e[X.id] - bi[X.id] + 1) * sum [i][X.id]);
}
}
if (Q.empty() && j != c + 1){
Q.push(node (sum[i][j],j));
bi[j] = 1;
}
}
}
printf ("%d",ans);
}