zoukankan      html  css  js  c++  java
  • 【DP】解析悬线法

    例题传送门:https://www.luogu.com.cn/problem/P4147
    大意:给出一张格子图,部分格子有障碍物,对于格子图上的所有矩形,我们称不包含障碍物的矩形为合法的,求合法的矩形的最大面积

    核心

    对于每个点 ((i,j)) ,求出其向上扩张最大距离后所能围成都最大矩形面积,则答案为 所有点向上扩张最大距离后所能围成的最大矩形面积所构成的集合中的最大值。

    ((i,j)) 指第 (i) 行,第 (j) 列的点。

    如何理解上面那句话?
    如图:黑格子为障碍物,蓝色格子为当前点。

    向上扩张后(红色格子为扩张区域),我们得到:

    红色和蓝色部分是不是很像从上面(图的上边界或当前点向上数的第一个障碍点)垂下来的线?悬线法因此得名。

    然后这条“线”向左右最大地扩张得到的面积就是蓝色格子(当前点)的向上扩张最大距离后所能围成都最大矩形面积

    如图:(所有绿色格子对应所求的面积)

    证明:所有点向上扩张最大距离后所能围成都最大矩形面积所构成的集合包含合法的矩形的最大面积
    假设合法的矩形的最大面积对应的矩形不在上述集合里,那么对应的矩形一定还可以向上扩张,得到的面积更大,矛盾。

    具体步骤

    定义:
    (l[i][j]) 为点 ((i,j)) 向上扩张后所对应的线能够所能到达的最左边的位置
    (r[i][j]) 为点 ((i,j)) 向上扩张后所对应的线能够所能到达的最右边的位置
    (u[i][j]) 为点 ((i,j)) 可以向上扩张的最大距离(包括自己)。

    预处理:
    求出每个点能够向左、向右(这里不是向上扩张的线,而是当前点)走到的最远点对应的位置,以及向上走的最远距离。

    void init(){
    	// init l[][]
    	for(int i=1;i<=n;i++)
    		for(int j=2;j<=m;j++)
    			if(g[i][j-1]) l[i][j]=l[i][j-1];
    	
    	// init r[][]
    	for(int i=1;i<=n;i++)
    		for(int j=m-1;j;j--)
    			if(g[i][j+1]) r[i][j]=r[i][j+1];
    	
    	// init u[][]
    	for(int i=2;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(g[i-1][j]) u[i][j]=u[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]);
    

    该点产生的贡献(向上扩张最大距离后所能围成都最大矩形面积)就是:

    u[i][j]*(r[i][j]-l[i][j]+1)
    

    其他细节请结合代码理解:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=1005;
    int n,m;
    bool g[N][N];
    int l[N][N],r[N][N],u[N][N]; 
    
    void init(){
    	// init l[][]
    	for(int i=1;i<=n;i++)
    		for(int j=2;j<=m;j++)
    			if(g[i][j-1]) l[i][j]=l[i][j-1];
    	
    	// init r[][]
    	for(int i=1;i<=n;i++)
    		for(int j=m-1;j;j--)
    			if(g[i][j+1]) r[i][j]=r[i][j+1];
    	
    	// init u[][]
    	for(int i=2;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(g[i-1][j]) u[i][j]=u[i-1][j]+1;
    }
    
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			char ch; cin>>ch;
    			g[i][j]= ch=='F'?1:0;
    			l[i][j]=r[i][j]=j;
    			u[i][j]=1;
    		}
    		
    	init(); // 预处理出每个点能够向左、向右、走到的最远点对应的位置,以及向上走的最远距离。
    	
    	int res=0;
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			if(!g[i][j]) continue;
    			if(i>1 && g[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]);
    			}
    			res=max(res,u[i][j]*(r[i][j]-l[i][j]+1));	
    		}
    	}
    		
    	cout<<res*3<<endl;
    	
    	return 0;
    }
    
  • 相关阅读:
    jQuery 源码解析(二十四) DOM操作模块 包裹元素 详解
    jQuery 源码解析(二十三) DOM操作模块 替换元素 详解
    jQuery 源码解析(二十二) DOM操作模块 复制元素 详解
    jQuery 源码分析(二十一) DOM操作模块 删除元素 详解
    jQuery 源码分析(二十) DOM操作模块 插入元素 详解
    jQuery 源码分析(十九) DOM遍历模块详解
    python 简单工厂模式
    python 爬虫-协程 采集博客园
    vue 自定义image组件
    微信小程序 image组件坑
  • 原文地址:https://www.cnblogs.com/Tenshi/p/14559271.html
Copyright © 2011-2022 走看看