【问题标题】:Finding rectangle with largest area given a set of line segments给定一组线段,找到面积最大的矩形
【发布时间】:2019-07-24 02:08:58
【问题描述】:

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

例如,当给定以下线段的集合时,结果应该是绿色阴影区域的区域:

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

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

【问题讨论】:

    标签: algorithm data-structures time-complexity big-o dynamic-programming


    【解决方案1】:

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

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

    现在取每个交点,假设它是一个矩形的左下角。对于其垂直线上的每个交叉点,请查看相交的水平线并找到这条线上最右边的点,您至少可以向下到达交叉点。您已经建立了一棵树,它允许您在该线上的交叉点数量中及时找到对数:从树的顶部开始,如果该孩子的值至少与您需要的一样远,则向右走,否则向左走。找到它会给你使用左下角和水平线的最大矩形,因此检查每条水平线会给你最大的矩形,包括左下角的交叉点,并为每个交叉点重复这个给你整体最大的矩形。

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

    【讨论】:

      【解决方案2】:

      你提供的例子:

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

      ---------------------
      |                   |
      |                   |
      |                   |
      |                   |
      ---------           ------------------
              |                            |
              |____________________________|
      

      那么问题就变成了在直线(又称正交)多边形中找到最大的矩形,这方面的文献很多。

      【讨论】:

        【解决方案3】:

        你可以使用线扫描算法来解决这个问题。

        在这种情况下,垂直线被添加或从线组中删除以在向上移动时考虑。线的起点和终点都添加到扫描集中,水平线添加到列表中。

        • 第一步:将行添加到activeVertical
        • 第 2 步:将第二行添加到 activeVertical
        • 第 3 步:将第三行添加到 activeVertical(注意:它们按 X 的顺序排列)。
        • 步骤 4a:将第四行添加到 activeVertical
        • 步骤 4b:找到水平线,是时候创建一个没有 有任何高度
        • 第 5 步:找到第二条水平线,检查完成前一个矩形的时间

        等等

        在代码下方 (C#)。余可以在这里找到更多关于线扫描算法的详细信息:https://en.wikipedia.org/wiki/Sweep_line_algorithm

        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++;
                    }
                }
            }
        }
        

        }

        【讨论】:

          猜你喜欢
          • 2011-06-30
          • 1970-01-01
          • 1970-01-01
          • 2011-06-02
          • 1970-01-01
          • 1970-01-01
          • 2014-02-08
          • 1970-01-01
          • 2017-04-21
          相关资源
          最近更新 更多