用于找到最少矩形以覆盖一组矩形而不重叠的算法

时间:2011-05-07 05:21:13

标签: algorithm language-agnostic geometry rectangles

我有一组矩形,我想“缩小”这个集合,所以我用最少的矩形来描述与原始集合相同的区域。如果可能的话,我希望它也快,但我更关心的是尽可能减少矩形的数量。我现在有一种方法,大部分时间都可以使用。

目前,我从最左上角的矩形开始,看看我是否可以在保持矩形的同时向右和向下展开它。我这样做,直到它不能再展开,删除并拆分所有相交的矩形,并在列表中添加展开的矩形。然后我再次使用下一个左上角的矩形启动该过程,依此类推。但在某些情况下,它不起作用。例如: enter image description here

使用这组三个矩形,正确的解决方案将最终得到两个矩形,如下所示: enter image description here

然而,在这种情况下,我的算法从处理蓝色矩形开始。这会向下扩展并分割黄色矩形(正确)。但是当处理黄色矩形的剩余部分时,它不是向下扩展,而是首先向右扩展并收回先前分离的部分。然后处理最后一个矩形,它不能向右或向下扩展,因此保留原始的矩形集。我可以调整算法,先向下扩展然后向右扩展。这将解决这种情况,但它会在翻转的类似场景中引起同样的问题。

编辑:为了澄清,原始的矩形集不会重叠,也不必连接。如果连接了一个矩形子集,那么完全覆盖它们的多边形可以在其中有孔。

4 个答案:

答案 0 :(得分:118)

尽管问题的标题,我认为你实际上是在寻找最小的解剖到直线多边形的矩形中。 (Jason的链接是关于矩形的最小覆盖,这是一个完全不同的问题。)

David Eppstein在2010年调查文章Graph-Theoretic Solutions to Computational Geometry Problems的第3部分讨论了这个问题,并在this answer on mathoverflow.net中提供了一个很好的摘要:

  

我们的想法是找到具有两个凹顶点作为端点的不相交的轴平行对角线的最大数量,沿着这些顶点分割,然后为每个剩余的凹顶点形成一个更多的分割。要找到不相交的轴平行对角线的最大数量,请形成对角线的交点图;这个图是二分的,因此它的最大独立集可以通过图匹配技术在多项式时间中找到。

使用Eppstein的文章中的图2,这是我对这个令人钦佩的简洁描述的光泽。假设我们有一个直线多边形,可能带有孔。

当多边形被解剖成矩形时,每个凹顶点必须被解剖的至少一个边缘满足。因此,如果尽可能多的这些边做双重任务,即它们连接两个凹顶点,我们得到最小解剖。

因此,让我们在两个完全包含在多边形内的凹顶点之间绘制轴平行对角线。 ('轴平行'在这里表示'水平或垂直',而diagonal of a polygon是连接两个非相邻顶点的线。)我们希望在解剖中使用尽可能多的这些线,只要它们不相交。

(如果没有轴平行的对角线,解剖是微不足道的 - 只需从每个凹顶点切割。或者如果轴平行对角线之间没有交叉点,那么我们全部使用它们,加上每个剩下的凹顶点。否则,请继续阅读。)

一组线段的intersection graph具有每个线段的节点,并且如果线交叉,则边连接两个节点。这是轴平行对角线的交叉图:

bipartite一部分是垂直对角线,另一部分是水平对角线。现在,我们希望尽可能多地选择对角线,只要它们不相交即可。这相当于在交叉图中找到maximum independent set

在一般图中找到最大独立集是NP难问题,但在二分图的特殊情况下,König’s theorem表明它等同于找到最大匹配的问题,可以是在多项式时间内求解,例如Hopcroft–Karp algorithm。给定的图形可以有几个最大匹配,但它们中的任何一个都可以,因为它们都具有相同的大小。在该示例中,所有最大匹配都有三对顶点,例如{(2,4),(6,3),(7,8)}:

(此图中的其他最大匹配项包括{(1,3),(2,5),(7,8)}; {(2,4),(3,6),(5,7)} ;和{(1,3),(2,4),(7,8)}。)

要从最大匹配到相应的minimum vertex cover,请应用proof of König’s theorem。在上面显示的匹配中,左集是 L = {1,2,6,7},右集是 R = {3,4,5,8 }, L 中不匹配的顶点集是 U = {1}。在 U 中只有一个交替路径,即1-3-6,因此交替路径中的顶点集合是 Z = {1,3,6}和因此,最小顶点覆盖 K =( L \ Z )∪( R Z < / em>)= {2,3,7},以红色显示,最大独立设置为绿色:

将此转换回解剖问题,这意味着我们可以在解剖中使用五个轴平行的对角线:

最后,从每个剩余的凹顶点进行切割以完成解剖:

答案 1 :(得分:8)

以下是一些讨论此问题解决方案的学术论文;

A Linear-Time Approximation Algorithm for Minimum Rectangular Covering(这是用于覆盖多边形,因此它比您在此处提供的更为一般)。

Optimal Rectangle Covers for Convex Rectinlinear Polygons(这是一个更符合您具体问题的方法)

您还可以尝试here参考有关此主题的更多论文的参考书目。

答案 2 :(得分:1)

感谢这篇对我帮助很大的出版物。

根据这些 2 件事:

1 我真的很傻。 2 我是一个糟糕的程序员。

我已经成功地遵循了这些步骤,用许多正交对其进行了测试,现在 我得到了一组困惑的矩形。

好的,但现在我真的很难用它来计算矩形列表。

一个刻薄的谈话或课程。

提前致谢。

让-伊夫

答案 3 :(得分:0)

今天我找到了这个问题的 O(N^5) 解决方案,在这里分享一下。

第一步,您需要找到一种方法来获得矩阵中任意矩形的和,复杂度为 O(1)。这很容易做到。

现在进行第二步,您需要了解动态规划。这个想法是存储一个矩形并将其分成更小的部分。如果矩形为空,则返回 0。如果矩形已填充,则返回 1。

有 N^4 个状态来存储矩形,加上每个状态的 O(N) 复杂度......所以你会得到一个 O(N^5) 算法。

这是我的代码。我认为这会有所帮助。

输入很简单。 N, M (矩阵大小) 之后,接下来的 N 行将有 1 和 0。 示例:

4 9
010000010
111010111
101111101
000101000
#include <bits/stdc++.h>
#define MAX 51
int tab[MAX][MAX];
int N,M;
int sumed[MAX][MAX];
int t(int x,int y) {
    if(x<0||y<0)return 0;
    return sumed[x][y];
}
int subrec(int x1,int y1,int x2,int y2) {
    return t(x2,y2)-t(x2,y1-1)-t(x1-1,y2)+t(x1-1,y1-1);
}
int resp[MAX][MAX][MAX][MAX];
bool exist[MAX][MAX][MAX][MAX];
int dp(int x1,int y1,int x2,int y2) {
    if(exist[x1][y1][x2][y2])return resp[x1][y1][x2][y2];
    exist[x1][y1][x2][y2]=true;
    int soma = subrec(x1,y1,x2,y2);
    int area = (x2-x1+1)*(y2-y1+1);
    if(soma==area){return resp[x1][y1][x2][y2]=1;}
    if(!soma) {return 0;}
    int best = 1000;
    for(int i = x1;i!=x2;++i) {
        best = std::min(best,dp(x1,y1,i,y2)+dp(i+1,y1,x2,y2));
    }
    for(int i = y1;i!=y2;++i) {
        best = std::min(best,dp(x1,y1,x2,i)+dp(x1,i+1,x2,y2));
    }
    return resp[x1][y1][x2][y2]=best;
}
int main()
{
    std::cin >> N >> M;
    for(int i = 0; i != N;++i) {
        std::string s;
        std::cin >> s;
        for(int j = 0; j != M;++j) {
            if(s[j]=='1')tab[i][j]++;
        }
    }
    for(int i = 0; i != N;++i) {
        int val = 0;
        for(int j = 0; j != M;++j) {
            val += tab[i][j];
            sumed[i][j]=val;
            if(i)sumed[i][j]+=sumed[i-1][j];
        }
    }
    std::cout << dp(0,0,N-1,M-1) << std::endl;
}