【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;
}
posted @   HinanawiTenshi  阅读(74)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示