zoukankan      html  css  js  c++  java
  • 悬线法 学习笔记

    之前的考试包括做题都有用过这个方法,算是个套路了。写一篇博客总结一下。

    ----------------

    悬线法的用途

    针对求给定矩阵中满足某条件的极大矩阵,比如“面积最大的长方形、正方形”“周长最长的矩形等等”。可以满足在时间复杂度为O(M*N)的要求,比一般的枚举高效的多,也易于理解。

    悬线法的思路

    悬线法,悬线的定义,就是一条竖线,这条竖线要满足上端点在整个矩形上边界或者是一个障碍点。然后以这条悬线进行左右移动,直到移至障碍点或者是矩阵边界,进而确定这条悬线所在的极大矩阵。也就是说,我们要针对矩阵中每个点进行求极大矩阵的操作。所以我们要$left$数组来存每个点能到达的最左的地方,$right$数组存每个点能到达的最右的地方。同时设置一个$up$数组表示最上能到达的地方,用来存矩形的上边界。有转移:

    $r[i][j]=r[i][j+1](a[i][j]==a[i][j+1])$

    $l[i][j]=l[i][j-1](a[i][j]==a[i][j-1])$

    $up[i][j]=up[i-1][j]+1(a[i][j]==a[i-1][j])$

    同时,在$up$进行转移的同时$left$和$right$要进行更新。

    例题

    1.棋盘制作

    裸题了。直接上套路即可。

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int N=2005;
    int l[N][N],r[N][N],up[N][N],a[N][N],n,m,ans1,ans2;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int main()
    {
        n=read();m=read();
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++)
            {
                a[i][j]=read();
                l[i][j]=r[i][j]=j;
                up[i][j]=1;
            }
        for (int i=1;i<=n;i++)
            for (int j=2;j<=m;j++)
                if (a[i][j]!=a[i][j-1])
                    l[i][j]=l[i][j-1];
        for (int i=1;i<=n;i++)
            for (int j=m-1;j>=1;j--)
                if (a[i][j]!=a[i][j+1])
                    r[i][j]=r[i][j+1];
        for (int i=2;i<=n;i++)
            for (int j=1;j<=m;j++)
            {
                if (a[i][j]!=a[i-1][j])
                {
                    l[i][j]=max(l[i][j],l[i-1][j]);
                    r[i][j]=min(r[i][j],r[i-1][j]);
                    up[i][j]=up[i-1][j]+1;
                }
                int a=r[i][j]-l[i][j]+1;
                int b=min(a,up[i][j]);
                ans1=max(ans1,b*b);
                ans2=max(ans2,a*up[i][j]);
            }
        printf("%d
    %d",ans1,ans2);
        return 0;
    }

    2.奶牛浴场

    这题在洛谷的数据加强后好多题解都被hack掉了。连wzk大佬论文里面的代码也有bug233。

    大体的思想是按照横坐标排序后选取两个点作为矩形的两个角计算面积,同时更新上下边界。从左到右扫完后从右到左再扫一遍。最后还有按照纵坐标排序再扫一遍。证明可以看洛谷第二篇题解。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=5050;
    int l,w,n,ans;
    struct node
    {
        int x,y;
    }a[N];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    bool cmp1(node x,node y)
    {
        return x.x<y.x;
    }
    bool cmp2(node x,node y)
    {
        return x.y<y.y;
    }
    int main()
    {
        l=read();w=read();n=read();
        for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read();
        a[++n].x=0;a[n].y=0;
        a[++n].x=0;a[n].y=w;
        a[++n].x=l;a[n].y=0;
        a[++n].x=l;a[n].y=w;
        int x1,y1,x2,y2;
        sort(a+1,a+n+1,cmp1);
        for (int i=1;i<=n;i++)
        {
            x1=a[i].x,y1=0,y2=w;
            for (int j=i+1;j<=n;j++)
            {
                x2=a[j].x;
                ans=max(ans,(x2-x1)*(y2-y1));
                if (a[j].y<a[i].y) y1=max(y1,a[j].y);
                else y2=min(y2,a[j].y);
            }
        }
        for (int i=n;i>=1;i--)
        {
            x1=a[i].x,y1=0,y2=w;
            for (int j=i-1;j>=1;j--)
            {
                x2=a[j].x;
                ans=max(ans,(x1-x2)*(y2-y1));
                if (a[j].y<a[i].y) y1=max(y1,a[j].y);
                else y2=min(y2,a[j].y);
            }
        }
        sort(a+1,a+n+1,cmp2);
        for (int i=1;i<n;i++)
            ans=max(ans,l*(a[i+1].y-a[i].y));
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    【WPF】城市级联(XmlDataProvider)
    【C#】利用反射构建实体
    毕业档案保存
    【WPF】淡入淡出切换页面
    【C#】Lamada表达式演变过程
    【C#】实现INotifyPropertyChanged的3种方法
    【Unity】矩阵运算
    (win7/8/10)鼠标右键添加按下SHIFT键时弹出带管理员权限的“在此处打开命令窗口”
    如何给grldr.mbr和grldr改名
    常用的时间同步服务器地址
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13775525.html
Copyright © 2011-2022 走看看