• 2019牛客多校训练第二场H. Second Large Rectangle (单调栈 / 悬线法)


    题目传送门

    题意

    输入整数n和m(n和m均∈[1,1000]),给出一个n×m的01矩阵,求第二大全1矩阵的面积。(如果矩阵少于两个1,则输出0)

    题解

    ①单调栈做法【复杂度O(n×m)】先预处理每个点(i,j)的高up[i][j],枚举每一行作为底边,遍历每一个点j,维护单调递减栈计算当前高度可达的最大区间(即当前可形成的全1矩阵的最大宽),宽×高即得当前可形成的全1矩形的面积,此时如果记录最大面积然后不断更新最优解即可求得所给01矩阵的最大全1矩阵面积。

    但是题目要求的是第二大,所以本蒟蒻天真的认为只要把所有面积记录到一个数组,之后partial_sort排下序后输出第二大就OK了。too young too simple~

    在经历过一次次WA之后去查了下,说是会存在第二大矩阵只包含在最大矩阵里面这种情况,奈何构造不出符合情况的测试用例来验证所以不太理解。最终去写了个生成随机测试用例,拿AC代码对拍了下,发现了如下两个测试用例存在错误。

    6 6             2 24
    100101   正确答案:5   101010001011100001001101     正确答案:5
    011011   我的答案:4    110111111001001100010101      我的答案:4
    111111
    010100
    011010
    001110

    所以要求第二大的矩阵面积,除了宽×高算面积外,还要再多算两种情况:(宽-1)×高,宽×(高-1)。

    Code

    /*133ms*/
    #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1005; int a[maxn][maxn],up[maxn][maxn],ans[3*maxn*maxn];//注意这里要3*maxn*maxn int main() { int n,m; while(~scanf("%d%d",&n,&m)){ memset(up,0,sizeof(up)); memset(a,0,sizeof(a)); memset(ans,0,sizeof(ans)); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%1d",&a[i][j]); up[i][j]=(a[i][j]==1)?up[i-1][j]+1:0; } } int area,q=0; for(int i=1;i<=n;i++){ stack<pair<int,int> >S; for(int j=1;j<=m+1;j++){ int L=j; while(!S.empty()&&up[i][j]<=S.top().first){ L=S.top().second; area=(j-L)*S.top().first; ans[q++]=area; area=(j-L-1)*S.top().first; ans[q++]=area; area=(j-L)*(S.top().first-1); ans[q++]=area; S.pop(); } if(S.empty()||up[i][j]>S.top().first) S.push({up[i][j],L}); } } partial_sort(ans,ans+2,ans+q,greater<int>()); cout<<ans[1]<<endl; } return 0; }

     求面积次大值除了部分排序或优先队列外,还可如下操作:

    1 void calc(int area)//求次大值(max1存最大值,max2存次大值)
    2 {
    3     if(area>max1){
    4         max2=max1;
    5         max1=area;
    6     }
    7     else if(area>max2)
    8         max2=area;
    9 }

    ②悬线法【复杂度O(n×m)】一般用来求解满足条件的最大子矩阵。(浅谈用极大化思想解决最大子矩阵问题

    悬线:上端点覆盖了一个障碍点或达到整个矩形上端的有效竖线。每条悬线向左和向右能到达的最远的点之间距离×悬线长度即为以这条悬线下端点为下边界的极大子矩阵。

    故该题用悬线法思路如下:

    对于每个点1,先L(i,j)预处理每个点1的第一个左障碍点0,R(i,j)预处理每个点1第一个右障碍点0,up(i,j)预处理每个点的高度;

    对于每个点进行状态转移:L(i,j)=max(L(i,j),L(i−1,j)) 该点左边第一个障碍点位置,边界也是障碍点
                R(i,j)=min(R(i,j),R(i−1,j)) 该点右边第一个障碍点位置,边界也是障碍点 

    故对于点(i,j)为底的悬线对应的子矩形,它的面积area=(R[i,j]-L[i,j]+1)*up[i,j]。

    至于如何求次大矩形面积,详见以下代码。

    Code:

    /*149ms*/
    #include<bits/stdc++.h> using namespace std; const int maxn=1e3+5; int a[maxn][maxn],up[maxn][maxn],L[maxn][maxn],R[maxn][maxn]; int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%1d",&a[i][j]); if(a[i][j]==0)continue; up[i][j]=up[i-1][j]+1;//预处理每个点的高 L[i][j]=(a[i][j-1]==1)?L[i][j-1]:j;//预处理每个点1的左边第一个障碍点0 } for(int j=m;j>=1;j--){ if(a[i][j]==0)continue; R[i][j]=(a[i][j+1]==1)?R[i][j+1]:j;//预处理每个点1右边的第一个障碍点0 } } int area,max1=0,r,l,u,b;//max1存最大矩形面积 for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ area=0; if(a[i][j]==0)continue; if(a[i-1][j]==1){ R[i][j]=min(R[i][j],R[i-1][j]); L[i][j]=max(L[i][j],L[i-1][j]); } area=(R[i][j]-L[i][j]+1)*up[i][j];//宽×高求得可形成的矩形面积 max1=max(max1,area);//更新面积的最大值 if(max1==area) r=R[i][j],l=L[i][j],u=up[i][j],b=i;//记录最大矩形的信息 } } int ans2=0; ans2=max(ans2,(r-l)*u);//特判最大的矩形少一行和少一列的情况 ans2=max(ans2,(r-l+1)*(u-1)); for(int i=1;i<=n;i++){ //求次大矩形面积 for(int j=1;j<=m;j++){ if(u==up[i][j]&&l==L[i][j]&&r==R[i][j]&&b==i)//跳过最大矩形 continue; area=(R[i][j]-L[i][j]+1)*up[i][j]; if(ans2<area) ans2=area; } } cout<<ans2<<endl;//输出次大矩形面积 return 0; }
  • 相关阅读:
    20192420 任文朗 202120222 《网络与系统攻防技术》实验一实验报告
    20192420 任文朗 202120222 《网络与系统攻防技术》实验二实验报告
    20192420 任文朗 202120222 《网络与系统攻防技术》实验三实验报告
    计算机网络基础概念
    计算机网络应用层
    计算机网络网络层
    计算机网络传输层
    计算机网络安全
    计算机网络数据链路层和物理层
    kafka Net的用法
  • 原文地址:https://www.cnblogs.com/HOLLAY/p/11355539.html
Copyright © 2020-2023  润新知