• [单调栈]矩形牛棚


    题目描述

    到底是个资本家,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);
    }
  • 相关阅读:
    luogu4365 秘密袭击 (生成函数+线段树合并+拉格朗日插值)
    [模板]左偏树
    luogu4166 最大土地面积 (旋转卡壳)
    bzoj3168 钙铁锌硒维生素 (矩阵求逆+二分图最小字典序匹配)
    [模板]矩阵树定理
    [模板]快速傅里叶变换(FFT)
    [模板]Min_25筛
    [模板]杜教筛
    [模板]莫比乌斯反演
    DrawerLayout—侧拉栏的使用
  • 原文地址:https://www.cnblogs.com/lover-fucker/p/13566704.html
Copyright © 2020-2023  润新知