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

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. tpo-10 C1 How to get photographs exhibited

    第 1 段 1.Listen to a conversation between a student and her Photography professor. 听一段学生和摄影学教授的对话. 第 ...

  2. JAVA中 "\" 和 "/" 的区别

    1.在java中路径一般用”/” 2.linux.unix中的路径一般用”/” 3.windows中的路径一般用”\” 所以在java中写windows路径一般用”/”,或用“\”将”\”转义一下(& ...

  3. Font Awesome 完美的图标字体

    好久没来,虽说鄙人的人气不咋地,但还是很想念自己这一亩二分田地. 近期用在平台开发中,看着设计师摆开阵势,准备大画图标,想着自己将会很KUBI拼凑css-sprite图片,接着写一大堆 class^= ...

  4. C复合文字

    C99之前,可以传递数组,但是没有所谓的数组常量可供传递,于是新增了复合文字. 普通数组声明方法: int d[2]={10,20}; 复合文字声明: 与数组名相同,常量同时代表元素的地址. (int ...

  5. UVa 340 - Master-Mind Hints 解题报告 - C语言

    1.题目大意 比较给定序列和用户猜想的序列,统计有多少数字位置正确(x),有多少数字在两个序列中都出现过(y)但位置不对. 2.思路 这题自己思考的思路跟书上给的思路差不多.第一个小问题——位置正确的 ...

  6. POJ 1113 Wall(计算几何の凸包)

    Description Once upon a time there was a greedy King who ordered his chief Architect to build a wall ...

  7. 关于set和get机制的整理

    首先这是es5新增的:定义是设置和获取对象属性时候出发的方法,属于修饰器: 犀牛书例子: function test(n){ return { get count(){ return n }, set ...

  8. 针对XX系统的可用性和易用性构想

    可用性是与系统故障有关的一个质量属性,是指系统正常运行的时间的比例,一般通过两次故障之间的时间长度或在系统崩溃情况下能恢复正常运行的速度来衡量,同时此概念涉及一个公式的计算,就是系统正常运行时间的百分 ...

  9. [贪心经典算法]Kruskal算法

    Kruskal算法的高效实现需要一种称作并查集的结构.我们在这里不介绍并查集,只介绍Kruskal算法的基本思想和证明,实现留在以后讨论. Kruskal算法的过程: (1) 将全部边按照权值由小到大 ...

  10. ubuntu软件管理apt与dpkg

    目前ubuntu系统主要有dpkg和apt两种软件管理方式两种区别如下 1.dpkg是用来安装.deb文件,但不会解决模块的依赖关系,且不会关心ubuntu的软件仓库内的软件,可以用于安装本地的deb ...