题注:最大子矩形问题的解决办法最初由中国国家集训队王知昆前辈整理并发表为论文,在此说明并感谢。

Definition

  给你一个大矩形,里面有一些障碍点,求一个面积最大的矩形,满足该矩形在大矩形内部且该矩形内部没有特殊点。矩形边界可以含有障碍点。这一类问题被称为最大子矩形问题。

Solution

  首先引用一些由王知昆前辈定义的概念:

    有效子矩形:合法的子矩形。

    极大子矩形:对于一个有效子矩形,如果不存在完全包含它的有效子矩形,则该矩形是一个极大子矩形。

    最大子矩形:所求的面积最大的有效子矩形

定理一:

    一个最大子矩形一定是一个极大子矩形。

证明:

      假设最大子矩形A不是极大子矩形,那么一定存在一个有效子矩形完全包含A,那么该矩形比A大且合法,则A不是最大子矩形。与假设矛盾。故一个最大子矩形一定是一个极大子矩形。

证毕。

定理二:

    最大子矩形的四周一定不能再向外拓展。具体地,最大子矩形的四周边界上要么存在障碍点,要么是大矩形的边界。

证明:

      假设最大子矩形的四周能向外扩展,那么它就不是一个极大子矩形。与【定理一】矛盾。

证毕。

  这样,我们直接考虑枚举所有的障碍点作为矩形边界,判断它是不是一个合法的矩形,如果是,则更新答案。

  记障碍点的个数是k,那么这么做的复杂度是\(O(k^5)\)。GG

  考虑我们枚举每个点作为矩形的左边界,不断向右枚举它的右边界,能否顺手处理它的上下边界?

  显然,对于左边界相同的矩形,右边界更靠右的矩形的上边界不高于靠左的矩形,下边界不低于更靠左的矩形。

  那么我们可以每次枚举一个障碍点作为左边界,向右枚举右边界,每次更新上边界和下边界。

  具体的方法为:每扫描到一个新的点,按照当前维护的上下边界计算面积并更新,更新后,如果当前点的竖直坐标高于左边界上的点,更新上边界,如果当前点的竖直坐标低于左边界上的点,更新右边界。

考虑特殊情况:如果两个点的竖直坐标相等,那么如何更新。

  在这种情况下如果继续更新上边界或下边界,那么左边界就会同时作为上边界或下边界。如图:

  

  

  将枚举分为两种情况:左侧障碍点只作为左边界;左侧障碍点同时作为左边界和上或下边界。

  对于第一种情况,遇到竖直坐标相等直接break。因为后面不会产生左侧障碍只作为左边界的矩形。

  对于第二种情况,每次扫描维护矩形向上拓展和向下拓展的最大值,不断更新。讲左侧障碍点作为上或下边界,不断更新面积。对于特殊情况,因为它在边界上对答案没有影响,直接忽略即可。

考虑是否将矩形枚举完全:我们只考虑了左边界是障碍点的情况,对于左边界是大矩形左侧的情况没有枚举到。

  对于这种矩形,分成两种情况:只有左侧是大矩形边界,右侧是障碍点;左右两侧都是大矩形边界。

  我们将大矩形四个角上四个点都加入到障碍点中,按照上面的算法,对于第一种情况可以通过被从右向左扫描一遍解决掉。

  下面讨论第二种情况:

定理三:

      一个左右边界都在大矩形的左右边界上的子矩形是极大子矩形充要条件是上下边界是按照竖直坐标排序后相邻的两个障碍点。

证明:

        充分性:假设上下边界的障碍点不是相邻的,那么显然存在k在两个点之间,从而在矩形内部,矩形不合法。

        必要性:假设子矩形的上下边界不是障碍点,那么它不是一个极大子矩形。

证毕。

  那么我们将障碍点按照竖直坐标排序,则他们之间矩形的面积是相邻两个障碍点的高度差和大矩形横向长度的乘积。每次更新答案即可。

Example

【P1578】奶牛浴场

description

由于John建造了牛场围栏,激起了奶牛的愤怒,奶牛的产奶量急剧减少。为了讨好奶牛,John决定在牛场中建造一个大型浴场。但是John的奶牛有一个奇怪的习惯,每头奶牛都必须在牛场中的一个固定的位置产奶,而奶牛显然不能在浴场中产奶,于是,John希望所建造的浴场不覆盖这些产奶点。这回,他又要求助于Clevow了。你还能帮助Clevow吗?

John的牛场和规划的浴场都是矩形。浴场要完全位于牛场之内,并且浴场的轮廓要与牛场的轮廓平行或者重合。浴场不能覆盖任何产奶点,但是产奶点可以位于浴场的轮廓上。

Clevow当然希望浴场的面积尽可能大了,所以你的任务就是帮她计算浴场的最大面积。

Input

输入文件的第一行包含两个整数L和W,分别表示牛场的长和宽。文件的第二行包含一个整数n,表示产奶点的数量。以下n行每行包含两个整数x和y,表示一个产奶点的坐标。所有产奶点都位于牛场内,即:\(0~\leq~x~\leq~L\),0\leqy\leqW。

Output

输出文件仅一行,包含一个整数S,表示浴场的最大面积。

Sample Input

10 10
4
1 1
9 1
1 9
9 9

Sample Output

80

Hint

\(0~\leq~n~\leq~5000\)

\(1~\leq~L,W~\leq~30000\)

Solution

板子题要啥solution?

Code

#include<cstdio>
#include<algorithm>
#define rg register
#define ci const int
#define cl const long long int typedef long long int ll; namespace IO {
char buf[100];
} template <typename T>
inline void qr(T &x) {
char ch=getchar(),lst=' ';
while(ch>'9'||ch<'0') lst=ch,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(lst=='-') x=-x;
} template <typename T>
inline void write(T x,const char aft,const bool pt) {
if(x<0) {putchar('-');x=-x;}
int top=0;
do {
IO::buf[++top]=x%10+'0';
x/=10;
}while(x);
while(top) putchar(IO::buf[top--]);
if(pt) putchar(aft);
} template <typename T>
inline T mmax(const T _a,const T _b) {if(_b<_a) return _a;return _b;}
template <typename T>
inline T mmin(const T _a,const T _b) {if(_a>_b) return _b;return _a;}
template <typename T>
inline T mabs(const T _a) {if(_a<0) return -_a;return _a;} template <typename T>
inline void mswap(T &_a,T &_b) {
T _temp=_a;_a=_b;_b=_temp;
} const int maxn = 35010; struct M {
int x,y;
inline M (int _a=0,int _b=0) {x=_a,y=_b;}
inline bool operator<(const M &_others) const {
return this->x<_others.x;
}
};
M MU[maxn]; inline bool cmp(const M &_a,const M &_b) {
return _a.y<_b.y;
} int l,w,n;
int ans; int main() {
qr(l);qr(w);qr(n);
for(rg int i=1;i<=n;++i) {
qr(MU[i].x);qr(MU[i].y);
}
MU[++n]=M(0,0);MU[++n]=M(l,0);MU[++n]=M(0,w);MU[++n]=M(l,w);
MU[0] = M(-1,-1);
std::sort(MU+1,MU+1+n,cmp);
for(rg int i=1;i<n;++i) {
rg int upceil = 0,downfloor = l;
rg int j=i+1;
while(MU[j].y==MU[i].y) ++j;
while(j<=n) {
ans=mmax(ans,(downfloor-upceil)*(MU[j].y-MU[i].y));
if(MU[j].x<MU[i].x) upceil=mmax(upceil,MU[j].x);
else if(MU[j].x>MU[i].x) downfloor=mmin(downfloor,MU[j].x);
else break;
++j;
}
j=i+1;upceil=0;downfloor=l;
while(MU[j].y==MU[i].y) ++j;
while(j<=n) {
ans=mmax(ans,(MU[j].y-MU[i].y)*mmax(MU[i].x-upceil,downfloor-MU[i].x));
if(MU[j].x<MU[i].x) upceil=mmax(upceil,MU[j].x);
else if(MU[j].x>MU[i].x) downfloor=mmin(downfloor,MU[j].x);
++j;
}
}
for(rg int i=n;i>1;--i) {
rg int upceil = 0,downfloor = l;
rg int j=i-1;
while(MU[j].y==MU[i].y) --j;
while(j) {
ans=mmax(ans,(downfloor-upceil)*(MU[j].y-MU[i].y));
if(MU[j].x<MU[i].x) upceil=mmax(upceil,MU[j].x);
else if(MU[j].x>MU[i].x) downfloor=mmin(downfloor,MU[j].x);
else break;
--j;
}
j=i-1;upceil=0;downfloor=l;
while(MU[j].y==MU[i].y) --j;
while(j) {
ans=mmax(ans,(MU[i].y-MU[j].y)*mmax(MU[i].x-upceil,downfloor-MU[i].x));
if(MU[j].x<MU[i].x) upceil=mmax(upceil,MU[j].x);
else if(MU[j].x>MU[i].x) downfloor=mmin(downfloor,MU[j].x);
--j;
}
}
rg int _temp=0;
std::sort(MU+1,MU+1+n);
for(rg int i=1;i<n;++i) {
_temp=mmax(_temp,MU[i+1].x-MU[i].x);
}
ans=mmax(ans,_temp*w);
write(ans,'\n',true);
}

Summary

这样的算法是有一定局限性的。当大矩形边界范围很大并且障碍点数较小时,使用本算法可以避免对坐标进行离散化降低代码难度并通过剪枝提升程序效率。但事实上,记大矩阵是 \(m~\times~n\)的矩阵,那么障碍点的个数上限会达到 \(m~\times~n\),\(m\)与\(n\)较大时讲难以接受。

存在一种只与矩形大小有关的算法。将在后面的blog介绍。

再次感谢王知昆前辈

【枚举】 最大子矩阵(I)的更多相关文章

  1. Hihocoder 1634 Puzzle Game(2017 ACM-ICPC 北京区域赛 H题,枚举 + 最大子矩阵变形)

    题目链接  2017 Beijing Problem H 题意  给定一个$n * m$的矩阵,现在可以把矩阵中的任意一个数换成$p$,求替换之后最大子矩阵的最小值. 首先想一想暴力的方法,枚举矩阵中 ...

  2. 崩 oj 1768 最大子矩阵

    描述 已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵.比如,如下4 * 4的矩阵0 -2 -7  0 9  2 -6  2 -4  1 - ...

  3. noi openjudge 1768:最大子矩阵

    链接:http://noi.openjudge.cn/ch0406/1768/ 描述已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵. 比如 ...

  4. [VijosP1764]Dual Matrices 题解

    题目大意: 一个N行M列的二维矩阵,矩阵的每个位置上是一个绝对值不超过1000的整数.你需要找到两个不相交的A*B的连续子矩形,使得这两个矩形包含的元素之和尽量大. 思路: 预处理,n2时间算出每个点 ...

  5. 【题解】Sonya and Matrix Beauty [Codeforces1080E]

    [题解]Sonya and Matrix Beauty [Codeforces1080E] 传送门:\(Sonya\) \(and\) \(Matrix\) \(Beauty\) \([CF1080E ...

  6. cogs 997. [東方S2] 射命丸文

    二次联通门 : cogs 997. [東方S2] 射命丸文 /* cogs 997. [東方S2] 射命丸文 二维前缀和 枚举每个子矩阵 更新最大值.. 莫名rank1 */ #include < ...

  7. 51nod 1051

    * 最大子矩阵 * sum[i][j] 表示第 i 行前 j 列的和,即每一行的前缀 * i,j 指针枚举列,k指针枚举行 * Now 记录当前枚举的子矩阵的价值 * 由于记录了前缀信息,一旦 Now ...

  8. 一些简单题(1)(Source : NOIP历年试题+杂题)

    最近也写了些许题目吧,还是写写博客,捋捋思路. P2216 [HAOI2007]理想的正方形 求一个$a \times b(a,b \leq 10^3)$的矩阵,求出一个$n \times n (n ...

  9. Codeforces Round #524 (Div. 2) codeforces 1080A~1080F

    目录 codeforces1080A codeforces 1080B codeforces 1080C codeforces 1080D codeforces 1080E codeforces 10 ...

  10. 2019杭电暑假多校训练 第六场 Snowy Smile HDU - 6638

    很多题解都是简单带过,所以打算自己写一篇,顺便也加深自己理解 前置知识:线段树.线段树维护最大字段和.二维坐标离散化 题解: 1.很容易想到我们需要枚举所有子矩阵来得到一个最大子矩阵,所以我们的任务是 ...

随机推荐

  1. 怎样下载Firefox与Chrome浏览器驱动

    在浏览器地址栏输入https://www.seleniumhq.org/ 打开Selenium官网 下载Firefox浏览器驱动 解压到本地 下载Chrome浏览器驱动 解压到本地 把这2个驱动放到P ...

  2. 【hidden】微信小程序hidden属性使用示例

    hidden属性用于隐藏标签,代码示例: <view hidden="{{!statusTag}}">我出来了~</view> <button bin ...

  3. javascript常用对象方法

    concat:连接产生一个新数组 [1,2].concat([3,4])     >> [1, 2, 3, 4] filter:返回符合条件的一个新数组 [1,2,3,4,5].filte ...

  4. Java学习笔记-11.运行期间类型鉴定

    1.Class对象的getClasses()方法获取的是该类中所有的公共的内部类,以及从父类,父接口继承来的内部类.getinterfaces()方法返回类继承的所有接口. import javax. ...

  5. Ubuntu16.04安装truffle时的一些错误

    1.使用truffle时出现 Error: /usr/bin/env: node: 没有那个文件或目录 1.如果是用sudo apt-get install nodejs命令安装的nodejs, ub ...

  6. 收割大厂offer需要具备的条件

    转载出处 本人也一直在关注互联网,觉得还是有些了解.互联网要求是越来越高了,竞争的人太多了,不过你不用担心,个人觉得,你到了中层的水平,拿二线offer应该没问题,人多也有人多的好处,我比别人多努力一 ...

  7. Mininet实验 多个数据中心的拓扑网络实现

    实验目的 掌握多数据中心网络拓扑的构建 掌握多数据中心数据交换过程 实验原理 主机间发送消息上报给交换机,交换机对收到的报文信息进行分析判断,如果交换机中存在此消息相对应的流表,则交换机直接下发流表, ...

  8. lintcode-172-删除元素

    172-删除元素 给定一个数组和一个值,在原地删除与值相同的数字,返回新数组的长度. 元素的顺序可以改变,并且对新的数组不会有影响. 样例 给出一个数组 [0,4,4,0,0,2,4,4],和值 4 ...

  9. TCP系列26—重传—16、重组包

    一.介绍 在TCP重传的时候,并没有限制TCP只能重传与初传完全相同的报文段大小,TCP允许执行重组包(repacketization),发送一个更大的TCP报文段,进而增加性能.TCP在重传时候允许 ...

  10. Uncaught ReferenceError: wx is not defined

    程序的分享功能调用了微信的接口,但是忽然发现就报这个错误, Uncaught ReferenceError: wx is not defined 同时下方还有这个错误 This content sho ...