• 修建泳池的题解


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

    对于每个点,我们算出 33 个数,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)

    dpdp 边求。

    现在我们就说说 dpdp 吧。

    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,ji,j 最多往上的长度做矩阵一条边,这个就是 upi,jup_{i,j} 干的事情,然后 lfti,jlft_{i,j}rgti,jrgt_{i,j} 则是要在 upi,jup_{i,j} 最优的情况下,让另一条边也最优。

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

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

    这就是我们 upup 做的事。

    213.png

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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RgSIt3ns-1586318433101)(https://i.loli.net/2020/03/25/z6ZwbUkCLTuRsPr.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++){
    			read(a[i][j]);
    			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);
    	return 0;
    }
    

    是不是就懂了qwqqwq。。。

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

  • 相关阅读:
    JavaScript深入之参数按值传递
    计算机网络:这是一份全面 & 详细 的TCP协议学习指南
    前端点击下载excel表格数据
    为什么选择器:last-child有时没有起作用?
    深入理解防抖和节流函数
    收集常用正则表达式
    深入研究-webkit-overflow-scrolling:touch及ios滚动
    一文搞懂网络知识,IP、子网掩码、网关、DNS、端口号
    正则替换replace中$1的用法
    数据库连接池性能对比
  • 原文地址:https://www.cnblogs.com/zhaohaikun/p/12816973.html
Copyright © 2020-2023  润新知