2019牛客暑期多校第二场H(悬线法)

悬线法  求解满足条件的最大子矩阵。

悬线:由一个点向下扩展所得到的合法线段。

L(i,j):从(i,j)向左扩展,最左端的合法位置。
R(i,j):从(i,j)向右扩展,最右端的合法位置。
h(i,j):从(i,j)(i,j)向上扩展,最上端的合法位置,即以(i,j)为下端点的悬线上端点。


状态转移 对于每个点(i,j)

若可以向上扩展,则
h(i,j)=h(i−1,j)+1
L(i,j)=max(L(i,j),L(i−1,j)) 该点最左边第一个合法的位置(即无障碍点)
R(i,j)=min(R(i,j),R(i−1,j))同理

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

该题即是求全1次大的子矩阵 详解见代码

需注意的就是有一个需要标记的地方,因为可能有多个子矩阵面积大小一样但位置不同

#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<queue> 
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1000000;
const int N=1005; 
int n,m;
int a[N][N];//障碍点为1 不是为0 
int L[N][N],R[N][N],l[N][N],r[N][N],h[N][N];
int ans=0;
priority_queue<int> q;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        for (int j=1;j<=m;++j){
            char ch;
            cin>>ch;
            if (ch=='0') a[i][j]=1;//障碍点 
            else a[i][j]=0;
        }
    for(int i=1;i<=n;++i){//预处理障碍点 
        int Max=0,Min=m+1;
        for(int j=1;j<=m;++j){
            if (a[i][j]) Max=max(Max,j);
            L[i][j]=Max;//左边第一个障碍点的位置 
        }
        for(int j=m;j>=1;--j){
            if (a[i][j]) Min=min(Min,j);
            R[i][j]=Min;//右边第一个障碍点的位置 
        }
    }
    for(int j=1;j<=m;++j) 
	    r[0][j]=m+1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j){
            if(a[i][j]){
                h[i][j]=0;
                l[i][j]=0,r[i][j]=m+1;
            }
            else{
                h[i][j]=h[i-1][j]+1;
                l[i][j]=max(l[i-1][j],L[i][j]);
                r[i][j]=min(r[i-1][j],R[i][j]);
            }
        }
    int tmp1=0;
	int ll=0;
	int rr=0;
	int hh=0;
	int bb=0;  
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j){
            int area=((r[i][j]-l[i][j]-1)*h[i][j]);
            if(tmp1<area){//找第一个最大的  标记长 宽 高 
            	ll=l[i][j];
            	rr=r[i][j];
            	hh=h[i][j];
            	bb=i;
            	tmp1=area;
	     }
        } 
 //   cout<<tmp1<<endl;    
    int tmp2=0;  
	tmp2=max(tmp2,(rr-ll-2)*hh);//特判最大的矩形下少一行或一列的面积 
	tmp2=max(tmp2,(rr-ll-1)*(hh-1));
	for(int i=1;i<=n;i++){//找次大 
		for(int j=1;j<=m;j++){
			if(hh==h[i][j]&&ll==l[i][j]&&rr==r[i][j]&&bb==i) continue;
			int area=((r[i][j]-l[i][j]-1)*h[i][j]);
			if(tmp2<area){
				tmp2=area;
			}
		}
	}    
    cout<<tmp2<<endl;
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值