Algorithm 在给定一组线段的情况下查找面积最大的矩形

Algorithm 在给定一组线段的情况下查找面积最大的矩形,algorithm,data-structures,time-complexity,big-o,dynamic-programming,Algorithm,Data Structures,Time Complexity,Big O,Dynamic Programming,假设我给了你一组线段,形式是[(x1,y1),(x2,y2)]。我们有两个点定义了线段。出于我们的目的,该部分将始终是水平或垂直的。我想找到由线段包围的任何矩形的最大面积 例如,当给定以下线段集时,结果应为绿色阴影区域的面积: 到目前为止,我能想到的唯一解决方案是蛮力——每一对水平段(O(N^2))都要在运行时用每一对垂直段(O(N^2))进行检查。显然,我们可以通过预先计算哪些段可以放在一起来优化这一点,但这仍然会使时间复杂度保持在O(N^4) 我正在寻找理想的O(N^2)解决方案,但如果您

假设我给了你一组线段,形式是
[(x1,y1),(x2,y2)]
。我们有两个点定义了线段。出于我们的目的,该部分将始终是水平或垂直的。我想找到由线段包围的任何矩形的最大面积

例如,当给定以下线段集时,结果应为绿色阴影区域的面积:

到目前为止,我能想到的唯一解决方案是蛮力——每一对水平段(
O(N^2)
)都要在运行时用每一对垂直段(
O(N^2)
)进行检查。显然,我们可以通过预先计算哪些段可以放在一起来优化这一点,但这仍然会使时间复杂度保持在
O(N^4)


我正在寻找理想的
O(N^2)
解决方案,但如果您有任何小于
O(N^4)
的解决方案,请与我们分享

您可以通过扫描找到垂直线和水平线之间的所有交点。按y递增的顺序遍历所有行。维护一个包含所有垂直线(包括当前y值)的缓冲区。保持缓冲区按每个垂直线的x值排序。当您到达每个水平线时,检查它是否与缓冲区中的任何线相交。最坏的情况是当有O(N^2)个交叉口时

现在您有了一个交叉点列表,以及每条线的交叉点列表。对于每一条水平线,对于每一个交叉点,我们感兴趣的是沿着该交叉点的垂直线可以走多远。将这些值存储在数组中。将这些值分成几对,并在数组中存储每对的最大值。对每个最大值重复此过程,依此类推。这将构建一个值树,其中叶是原始值,按原始顺序排列,每个节点都具有在任何子代中找到的最大值。总成本与交叉口数量成线性关系

现在取每个交点,假设它是矩形的左下角。对于其垂直线上的每个交点,查看相交的水平线,并找到该线上最右侧的点,在该点上,您可以向下至少到达交点。您已经构建了一棵树,它允许您在该直线上的交点数中找到该时间对数:从树的顶部开始,如果该子节点的值至少达到您需要的距离,则向右移动,否则向左移动。通过左下角和水平线查找此项,可以得到最大的矩形,因此对每条水平线检查此项,可以得到最大的矩形,包括左下角的交点,对每个交点重复此项,可以得到整体最大的矩形


如果这些线形成了一个nxn网格,那么对于每个交叉点,您将检查其上方的O(N)条水平线,成本为O(logn),因此在最坏的情况下,该阶段的总成本为O(N^3logn)。

您提供的示例:

实际上,一旦我们只提取和合并由交点形成的矩形,就会简化为这样:

---------------------
|                   |
|                   |
|                   |
|                   |
---------           ------------------
        |                            |
        |____________________________|
然后问题就变成了在直线(又称正交)多边形中寻找最大的矩形,关于这个问题有很多文献。

你可以使用直线扫描算法来解决这个问题。 在这种情况下,向上移动时,垂直线将从线集中添加或删除。线的起点和终点都将添加到扫掠集,水平线也将添加到列表中。

  • 步骤1:将线添加到activeVertical
  • 步骤2:添加到activeVertical的第二行
  • 步骤3:添加到activeVertical的第三行(注意:它们按X的顺序排列)
  • 步骤4a:添加到activeVertical的第四行
  • 第4b步:找到水平线,是时候创建一个不包含水平线的矩形了 有身高吗
  • 第5步:找到第二条水平线,检查完成上一个矩形的时间
等等

下面是代码(C#)。Yuo可以在此处找到有关线条扫描算法的更多详细信息:

使用系统;
使用System.Collections.Generic;
使用System.Linq;
名称空间tt
{
公共课点
{
公共点(双X,双Y)
{
这个.X=X;
这个。Y=Y;
}
公共双X{get;set;}
公共双Y{get;set;}
}
公共班级线
{
公共点开始{get;set;}
公共点结束{get;set;}
}
公共类矩形
{
公共矩形()
{ }
公共矩形(点左下角,点右上角)
{
this.BottomLeft=BottomLeft;
this.TopRight=TopRight;
}
公共点左下角{get;set;}
公共点右上方{get;set;}
}
公共类X比较者:I比较者
{
公共整数比较(x行,y行)
{
返回x.Start.x.CompareTo(y.Start.x);
}
}
公共课程
{
公共静态int GetMinIndex(列表行、水平行)
{
var xComp=new XComparer();
int minIndex=Lines.BinarySearch(水平,xComp);
如果(minIndex<0)minIndex=~minIndex;
返回minIndex;
}
公共静态int GetMaxIndex(列表行,水平行)
{
var xComp=new XComparer();
int maxIndex=Lines.BinarySearch(新行(){Start=Horizontal.End},xComp);
如果(maxIndex<0)maxIndex=~maxIndex-1;
返回最大索引;
}
公共静态void Main()
{
列表行=新列表();
Add(newline(){Start=newpoint(0.5,12.5),End=newpoint(10,12.5)});
添加(新行(){开始=新点(2.5,9.5),结束=新点,
using System;
using System.Collections.Generic;
using System.Linq;

namespace tt
{
    public class Point
    {
        public Point(double X, double Y)
        {
            this.X = X;
            this.Y = Y;
        }
        public double X { get; set; }
        public double Y { get; set; }
    }
    public class Line
    {
        public Point Start { get; set; }
        public Point End { get; set; }
    }

    public class Rectangle
    {
        public Rectangle()
        { }
        public Rectangle(Point BottomLeft, Point TopRight)
        {
            this.BottomLeft = BottomLeft;
            this.TopRight = TopRight;
        }
        public Point BottomLeft { get; set; }
        public Point TopRight { get; set; }
    }

    public class XComparer : IComparer<Line>
    {
        public int Compare(Line x, Line y)
        {
            return x.Start.X.CompareTo(y.Start.X);
        }
    }

    public class Program
    {
        public static int GetMinIndex(List<Line> Lines, Line Horizontal)
        {
            var xComp = new XComparer();
            int minIndex = Lines.BinarySearch(Horizontal, xComp);
            if (minIndex < 0) minIndex = ~minIndex;
            return minIndex;
        }

        public static int GetMaxIndex(List<Line> Lines, Line Horizontal)
        {
        var xComp = new XComparer();
        int maxIndex = Lines.BinarySearch(new Line() { Start = Horizontal.End }, xComp);
        if (maxIndex < 0) maxIndex = ~maxIndex - 1;
        return maxIndex;
    }
    public static void Main()
    {
        List<Line> lines = new List<Line>();
        lines.Add(new Line() { Start = new Point(0.5, 12.5), End = new Point(10, 12.5)  });
        lines.Add(new Line() { Start = new Point(2.5, 9.5), End = new Point(15.8, 9.5) });
        lines.Add(new Line() { Start = new Point(6, 8.5), End = new Point(16.3, 8.5) });
        lines.Add(new Line() { Start = new Point(3.5, 8.5), End = new Point(3.5, 12.5) });
        lines.Add(new Line() { Start = new Point(7, 4.2), End = new Point(7, 13.8) });
        lines.Add(new Line() { Start = new Point(10, 5.8), End = new Point(10, 14.2) });
        lines.Add(new Line() { Start = new Point(15.6, 0), End = new Point(15.6, 16) });
        lines.Add(new Line() { Start = new Point(1.6, 20), End = new Point(15.6, 20) });

        var activeVertical = new List<Line>();

        SortedList<double, List<Line>> sweepSet = new SortedList<double, List<Line>>();

        foreach (Line oneLine in lines.Where(x => x.Start.X == x.End.X))
        {
            if (!sweepSet.ContainsKey(oneLine.Start.Y)) sweepSet.Add(oneLine.Start.Y, new List<Line>());
            sweepSet[oneLine.Start.Y].Add(oneLine);

            if (!sweepSet.ContainsKey(oneLine.End.Y)) sweepSet.Add(oneLine.End.Y, new List<Line>());
            sweepSet[oneLine.End.Y].Add(oneLine);
        }

        var linesHorizontal = lines.Where(x => x.Start.Y == x.End.Y).OrderBy(x => x.Start.Y).ToList();

        List<Rectangle> rectangles = new List<Rectangle>();
        List<Rectangle> completedRectangles = new List<Rectangle>();
        var xComp = new XComparer();

        int horIndex = 0;
        int sweepIndex = 0;
        while (sweepIndex < sweepSet.Count)
        {
            double y = Math.Min(sweepSet.Keys[sweepIndex], linesHorizontal[horIndex].Start.Y);

            double verValue = linesHorizontal[horIndex].Start.Y;
            //add lines which are influencing
            if (sweepSet.ContainsKey(y))
            {
                foreach (Line oneLine in sweepSet[y].Where(x => x.Start.Y == y))
                {

                    int index = activeVertical.BinarySearch(oneLine, xComp);
                    if (index < 0) index = ~index;
                    activeVertical.Insert(index, oneLine);
               }
            }
            if (y == verValue)
            {
                int minIndex = GetMinIndex(activeVertical, linesHorizontal[horIndex]);
                int maxIndex = GetMaxIndex(activeVertical, linesHorizontal[horIndex]);


                if (minIndex != maxIndex && minIndex < activeVertical.Count && maxIndex < activeVertical.Count)
                {
                    double minX = activeVertical[minIndex].Start.X;
                    double maxX = activeVertical[maxIndex].Start.X;

                    foreach (Rectangle oneRec in rectangles)
                    {
                        if (minX > oneRec.BottomLeft.X) oneRec.BottomLeft.X = minX;
                        if (maxX < oneRec.TopRight.X) oneRec.TopRight.X = maxX;
                        oneRec.TopRight.Y = verValue;
                    }
                    completedRectangles.AddRange(rectangles);
                    rectangles.Clear();


                    rectangles.Add(new Rectangle(new Point(activeVertical[minIndex].Start.X, verValue), new Point(activeVertical[maxIndex].Start.X, verValue)));
                }
                else rectangles.Clear();
            }
            //Cleanup lines which end
            if (sweepSet.ContainsKey(y))
            {
                foreach (Line oneLine in sweepSet[y].Where(x => x.End.Y == y))
                {

                    activeVertical.Remove(oneLine);
                }
            }

            if (y >= verValue)
            {
                horIndex++;
                if (horIndex == linesHorizontal.Count) break;
                if (y == sweepSet.Keys[sweepIndex]) sweepIndex++;
            }
            else
            {
                sweepIndex++;
            }
        }
    }
}