• 最大子矩阵问题——悬线法


    最大子矩阵问题——悬线法

    最大子矩阵问题

    在一个给定的矩阵中,有若干个障碍点,求出不包含障碍点的最大子矩阵。

    定义

    有效子矩阵

    内部不包含任何障碍点且边界与坐标轴平行的子矩形。

    极大有效子矩阵

    一个有效子矩形,如果不存在包含它且比它大的有效子矩形,就称这个有效子矩形为极大有效子矩形。

    最大有效子矩阵

    所有有效子矩形中最大的一个(或多个)。

    悬线法

    顾名思义,就是用一根线来回的晃动,来求出最大的子矩阵。

    板子

    for(int i=1;i<=n;i++){//初始化
    	for(int j=1;j<=m;j++){
    		r[i][j]=l[i][j]=j;
    		u[i][j]=1;
    	}
    }
    for(int i=1;i<=n;i++){//初始向左的数组
    	for(int j=2;j<=m;j++){
    		if(满足条件){
    			l[i][j]=l[i][j-1];
    		}
    	}
    }
    for(int i=1;i<=n;i++){//初始向右的数组
    	for(int j=m-1;j>=1;j--){
    		if(满足条件){
    			r[i][j]=r[i][j+1];
    		}
    	}
    }
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=m;j++){
    		if(i>1){//由上方推出状态
    			if(满足条件){
    				l[i][j]=max(l[i][j],l[i-1][j]);
    				r[i][j]=min(r[i][j],r[i-1][j]);
    		 		u[i][j]=u[i-1][j]+1;
    		 	}
    		}
    		int x=r[i][j]-l[i][j]+1;
    		int y=min(x,u[i][j]);
    		ans=max(y,ans);
    	}
    }
    

    例题

    P4147 玉蟾宫

    直接将板子套上就可以了。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=1000+50;
    int n,m,ans;
    char a[maxn][maxn];
    int r[maxn][maxn],l[maxn][maxn],u[maxn][maxn];
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			scanf(" %c ",&a[i][j]);
    			r[i][j]=l[i][j]=j;
    			u[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]&&a[i][j]=='F'){
    				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]&&a[i][j]=='F'){
    				r[i][j]=r[i][j+1];
    			}
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			if(i>1){
    				if(a[i][j]==a[i-1][j]&&a[i][j]=='F'){
    					l[i][j]=max(l[i][j],l[i-1][j]);
    					r[i][j]=min(r[i][j],r[i-1][j]);
    			 		u[i][j]=u[i-1][j]+1;
    			 	}
    			}
    			int x=r[i][j]-l[i][j]+1;
    			int y=min(x,u[i][j]);
    			ans=max(x*u[i][j],ans);
    		}
    	}
    	cout<<ans*3<<endl;
    	return 0;
    }
    

    P1578 奶牛浴场

    一看数据,3e4,开朴素的二维数组肯定是开不下的。

    我们可以换一种思路,直接枚举每个障碍点,分别从左到右,从上往下枚举即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=5000+50;
    int n,m,s;
    struct Node{
    	int x,y;
    }a[maxn];
    
    bool cmp1(Node a,Node b){
    	return a.x<b.x;
    }
    
    bool cmp2(Node a,Node b){
    	return a.y<b.y;
    }
    
    int ans=0;
    
    int main(){
    	scanf("%d%d%d",&n,&m,&s);
    	for(int i=1;i<=s;i++){
    		scanf("%d%d",&a[i].x,&a[i].y);
    	}
    	a[++s].x=0;a[s].y=0;
    	a[++s].x=0;a[s].y=m;
    	a[++s].x=n;a[s].y=0;
    	a[++s].x=n;a[s].y=m;
    	sort(a+1,a+s+1,cmp1);
    	int up,down,v;
    	for(int i=1;i<=s;i++){
    		up=0;
    		down=m;
    		v=m-a[i].x;
    		for(int j=i+1;j<=s;j++){
    			if(v*(down-up)<=ans)break;
    			ans=max(ans,(down-up)*(a[j].x-a[i].x));
    			if(a[j].y>=a[i].y){
    				down=min(down,a[j].y);
    			}else{
    				up=max(up,a[j].y);
    			}
    		}
    	}
    	sort(a+1,a+s+1,cmp2);
    	int l,r;
    	for(int i=1;i<=s;i++){
    		l=0;
    		r=n;
    		v=m-a[i].y;
    		for(int j=i+1;j<=s;j++){
    			if(v*(r-l)<=ans)break;
    			ans=max(ans,(r-l)*(a[j].y-a[i].y));
    			if(a[j].x>=a[i].x){
    				r=min(r,a[j].x);
    			}else{
    				l=max(l,a[j].x);
    			}
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    HTML5的页面资源预加载技术(Link prefetch)加速页面加载
    正则表达式入门教程
    SQL 查询分析器操作(修改、添加、删除)表及字段等
    linux终端相关概念解释及描述
    linux系统编程--守护进程,会话,进程组,终端
    linux系统编程--信号
    linux系统编程--进程间通信
    linux系统编程--进程
    Python GUI编程(Tkinter)(一)
    C++中时间转换
  • 原文地址:https://www.cnblogs.com/Rubyonly233/p/13307881.html
Copyright © 2020-2023  润新知