zoukankan      html  css  js  c++  java
  • 最大矩形问题总结

    昨天晚上刷水题写到一道最大全1正方形,于是兴致勃勃将系列相关都复习了一遍qwq

    1.最大子段和 

    测试地址:https://www.luogu.org/problem/show?pid=1115

    Problem:给出一段序列,选出其中连续且非空的一段使得这段和最大。

    Key:f[i]表示以i为终点时的最大子段和,f[i]=max(f[i-1],0)+a[i];ans=max(ans,f[i]);

     1 #include<cmath>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 const int maxn=200009;
     7 int a[maxn],f[maxn];
     8 int main()
     9 {
    10     int n;
    11     scanf("%d",&n);
    12     for(int i=1;i<=n;i++)
    13     {
    14         scanf("%d",&a[i]);
    15     }
    16     for(int i=1;i<=n;i++)
    17     {
    18         f[i]=max(f[i-1],0)+a[i];
    19     }
    20     int ans=-0x3f3f3f3f;
    21     for(int i=1;i<=n;i++)
    22     {
    23         ans=max(ans,f[i]);
    24     }
    25     cout<<ans<<endl;
    26     return 0;
    27 }
    Problem#1

    2.最大子矩形和(最大加权矩形) 

    测试地址:https://www.luogu.org/problem/show?pid=1719

    Problem:给定一个整数矩阵,求其中一个任意大小的子矩形,使该子矩形内值的和最大。

    Key:将二维的矩阵按行压成一维,枚举列来做最大子段和,复杂度O(n^3)。

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cmath>
     4 #include<cstdio>
     5 #include<cstring>
     6 using namespace std;
     7 const int maxn=150;
     8 int a[maxn][maxn],n,m,sum[maxn];
     9 int ans=-0x3f3f3f3f;
    10 int f[maxn];
    11 int calc_max(int val[],int n)
    12 {
    13     int ret=-0x3f3f3f3f;
    14     f[1]=val[1];
    15     for(int i=2;i<=n;i++)
    16         f[i]=max(f[i-1],0)+val[i],ret=max(ret,f[i]);
    17     return ret;
    18 }
    19 
    20 int main()
    21 {
    22     scanf("%d",&n);
    23     m=n;
    24     for(int i=1;i<=n;i++)
    25     {
    26         for(int j=1;j<=m;j++)
    27         {
    28             scanf("%d",&a[i][j]);
    29         }
    30     }
    31     for(int i=1;i<=n;i++)
    32     {
    33         for(int k=1;k<=m;k++)
    34         {
    35             sum[k]=0;
    36         }
    37         for(int j=i;j<=n;j++)
    38         {
    39             int k;
    40             for(k=1;k<=m;k++)
    41             {
    42                 sum[k]+=a[j][k];
    43             }
    44             int mx=calc_max(sum,k);
    45             ans=max(ans,mx);
    46         }
    47     }
    48     cout<<ans<<endl;
    49     return 0;
    50 }
    Problem#2

    3.最大全1正方形

    测试地址:https://www.luogu.org/problem/show?pid=1387

    Problem:在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长。

    Key:

      解法1:暴力维护二维前缀和,枚举边长来更新ans,其中求二维前缀和是容斥原理。

     1 #include<iostream>
     2 #include<cmath>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<algorithm>
     6 using namespace std;
     7 const int maxn=109;
     8 int n,m,sum[maxn][maxn],ans=0;
     9 int main()
    10 {
    11     scanf("%d%d",&n,&m);
    12     for(int i=1;i<=n;i++)
    13     {
    14         for(int j=1;j<=m;j++)
    15         {
    16             int x;
    17             scanf("%d",&x);
    18             sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+x;
    19         }
    20     }
    21     for(int i=1;i<=n;i++)
    22     {
    23         for(int j=1;j<=m;j++)
    24         {
    25             for(int l=1;l+i<=n&&l+j<=m;l++)
    26             {
    27                 int ok=sum[i+l][j+l]-sum[i+l][j]-sum[i][j+l]+sum[i][j];
    28                 if(ok==l*l)
    29                 {
    30                     ans=max(ans,l);
    31                 }
    32             }
    33         }
    34     }
    35     printf("%d",ans);
    36     return 0;
    37 }
    Problem#3--解法1

      解法2:f[i][j]表示以(i,j)为右下角的时候,能延伸左上角到最大的的正方形。如果(i,j)这个点为0时直接不考虑,不为0时才计算转移方程f[i,j]=min(f[i-1,j-1],f[i-1,j],f[i,j-1]),转移方程的实际意义是右上方、左方、右方能扩展的最大边长+1 (加1的操作就是计算(i,j)本身)。

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cmath>
     4 #include<cstdio>
     5 #include<cstring>
     6 using namespace std;
     7 const int maxn=109;
     8 int f[maxn][maxn],n,m,ans=0;
     9 int main()
    10 {
    11     scanf("%d%d",&n,&m);
    12     for(int i=1; i<=n; i++)
    13     for(int j=1; j<=m; j++)
    14     {
    15         int x;
    16         scanf("%d",&x);
    17         if(x)//如果这一个格子是1
    18         {//f[i][j]表示以(i,j)为右下角的,能延伸左上角最大的(正方形)边长
    19             f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1;
    20             //取min是为了验证是不是三个格子都能达到扩展要求
    21             //如果其中一个值为0,说明那个角没法扩展正方形
    22             //如果其中最小值为x,说明这个点最多扩展x,加他自己就是+1
    23             ans=max(ans,f[i][j]);
    24         }
    25     }
    26     cout<<ans<<endl;
    27     return 0;
    28 }
    Problem#3--解法2

    4.最大全1矩形

    测试地址:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1158

    Problem:给出1个M*N的矩阵M1,里面的元素只有0或1,找出M1的一个子矩阵M2,M2中的元素只有1,并且M2的面积是最大的。输出M2的面积。

    Key:

      解法1:存储矩阵时,将非1的元素存成-inf,直接做一遍最大子矩形和。

      解法2:处理每一行的每一个元素能向下延伸的最大全1长度,这样得到每一行都是一个可以“histogram求最大矩形”的序列。主要做法是对于每一行,从左到右遍历一遍刚刚得到的序列,用f[i]表示以i为起点时,向右能扩展的最大矩形面积,最后在所有的f数组中取max。

      解法3:递推处理,f[i][j]表示第i行,第j列时,从i能延伸的最大全1长度。记录l和r两个数组,分别表示能向左或右扩展的最大位置。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <iostream>
     5 using namespace std;
     6 const int maxn=1009;
     7 int l[maxn],r[maxn];
     8 int f[maxn][maxn],n,m;
     9 int main()
    10 {
    11     
    12     scanf("%d%d",&n,&m);
    13     for(int i=1; i<=n; ++i)
    14         for(int j=1; j<=m; ++j)
    15         {
    16             int x;
    17             scanf("%d",&x);
    18             if(x==1)f[i][j]=f[i-1][j]+1;
    19             else f[i][j]=0;
    20         }
    21     int ans=0;
    22     for(int i=1; i<=n; ++i)
    23     {
    24         for(int j=1; j<=m; ++j)
    25             l[j]=r[j]=j;
    26         f[i][0]=f[i][m+1]=-1;
    27         for(int j=1; j<=m; ++j)
    28         {
    29             while(f[i][j]<=f[i][l[j]-1])
    30                 l[j]=l[l[j]-1];
    31         }
    32         for(int j=m; j>=1; --j)
    33         {
    34             while(f[i][j]<=f[i][r[j]+1])
    35                 r[j]=r[r[j]+1];
    36         }
    37         for(int j=1; j<=m; ++j)
    38         {
    39             if(f[i][j])
    40             {
    41                 int t=(r[j]-l[j]+1)*f[i][j];
    42                 if(t>ans)ans=t;
    43             }
    44         }
    45     }
    46     printf("%d
    ",ans);
    47     return 0;
    48 }
    Problem#4--解法3
  • 相关阅读:
    阿里IM技术分享(六):闲鱼亿级IM消息系统的离线推送到达率优化
    IM开发基础知识补课(十):大型IM系统有多难?万字长文,搞懂异地多活!
    长连接网关技术专题(六):石墨文档单机50万WebSocket长连接架构实践
    手把手教你实现网页端社交应用中的@人功能:技术原理、代码示例等
    跟着源码学IM(九):基于Netty实现一套分布式IM系统
    网络编程懒人入门(十三):一泡尿的时间,快速搞懂TCP和UDP的区别
    探探的IM长连接技术实践:技术选型、架构设计、性能优化
    直播系统聊天技术(六):百万人在线的直播间实时聊天消息分发技术实践
    基于实践:一套百万消息量小规模IM系统技术要点总结
    Datafram 实现作为正文发送邮件
  • 原文地址:https://www.cnblogs.com/Beckinsale/p/7691176.html
Copyright © 2011-2022 走看看