题解 P4147 【玉蟾宫】

这片土地被分成 \(N×M\) 个格子,每个格子里写着 'R' 或者 'F',R 代表这块土地被赐予了 rainbow,F 代表这块土地被赐予了 freda。

现在 freda 要在这里卖萌。。。它要找一块矩形土地,要求这片土地都标着 'F' 并且面积最大。

但是 rainbow 和 freda 的 OI 水平都弱爆了,找不出这块土地,而蓝兔也想看 freda 卖萌(她显然是不会编程的……),所以它们决定,如果你找到的土地面积为 \(S\),它们每人给你 \(S\) 两银子。

这道题,我们可以想一下,矩形的面积跟 \(2\) 条边有关。

对于每个点,我们算出 \(3\) 个数,lftrgtup

lft:即此点最多能向左延伸到哪一列。(初值为j)

for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		if(a[i][j]&&a[i][j-1])lft[i][j]=lft[i][j-1];

rgt:即此点最多能向右延伸多少哪一列。(初值为j)

for(int i=1;i<=n;i++)
	for(int j=m;j>=1;j--)
		if(a[i][j]&&a[i][j+1])rgt[i][j]=rgt[i][j+1];

up:即此点最多能向上延伸多少个格子数。(初值为1)

\(dp\) 边求。

现在我们就说说 \(dp\) 吧。

for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++){
		if(a[i][j]&&a[i-1][j]){
			lft[i][j]=max(lft[i-1][j],lft[i][j]);//在up最优的情况下,左端点的距离要更新
			rgt[i][j]=min(rgt[i-1][j],rgt[i][j]);//在up最优的情况下,右端点的距离要更新
			up[i][j]=up[i-1][j]+1;
		}
		int x=rgt[i][j]-lft[i][j]+1;
		ans=max(ans,x*up[i][j]);
	}

其实就是找高度最高的矩形,为什么这样是正确的呢。

我们可以这样想一下。我们的算法本质就是,\(i,j\) 最多往上的长度做矩阵一条边,这个就是 \(up_{i,j}\) 干的事情,然后 \(lft_{i,j}\)\(rgt_{i,j}\) 则是要在 \(up_{i,j}\) 最优的情况下,让另一条边也最优。

你可以再想一下,下面这张图,最大子矩阵是橙色方框圈起来的。我们可以发现,最大子矩阵的四条边,每条边要么是靠到边界,要么是靠到障碍物。也就是说,最大子矩阵的上边界一定会靠到障碍物或边界。我们的算法就相当于确定了下边界,然后用 \(up\) 数组又确定了上边界。然后用 \(lft\)\(rgt\) 确定左边界和右边界。
在这里插入图片描述

如果还是不懂的话,可以再想一下,我来模拟一下。

这就是我们 \(up\) 做的事。

213.png

然后呢,对于每条线,我们要让他们的上边界尽可能的长。

再回来看看代码:

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>void write(T x){
	if(x<0)putchar('-'),x*=-1;
	if(x>9)write(x/10);
	putchar(x%10+48);
}
int a[2010][2010],lft[2010][2010],rgt[2010][2010],up[2010][2010],ans;
int main(){
	int n,m;
	read(n);read(m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			char ch;
			cin>>ch;
			if(ch=='F')a[i][j]=1;
			lft[i][j]=j;
			rgt[i][j]=j;
			up[i][j]=1;
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(a[i][j]&&a[i][j-1])lft[i][j]=lft[i][j-1];
	for(int i=1;i<=n;i++)
		for(int j=m;j>=1;j--)
			if(a[i][j]&&a[i][j+1])rgt[i][j]=rgt[i][j+1];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			if(a[i][j]&&a[i-1][j]){
				lft[i][j]=max(lft[i-1][j],lft[i][j]);
				rgt[i][j]=min(rgt[i-1][j],rgt[i][j]);
				up[i][j]=up[i-1][j]+1;
			}
			int x=rgt[i][j]-lft[i][j]+1;
			ans=max(ans,x*up[i][j]);
		}
	write(ans*3);
	return 0;
}

是不是就懂了\(qwq\)。。。

我才不告诉你,这个其实就是单调队列

posted @ 2020-10-17 09:49  zhaohaikun  阅读(113)  评论(0编辑  收藏  举报