想象一下,我给了您一组[(x1, y1), (x2, y2)]
形式的线段。我们有两个定义线段的点。就我们的目的而言,此段始终为水平或垂直。我想找到由线段包围的任何矩形的最大面积。
例如,当给定以下线段集时,结果应为绿色阴影区域:
到目前为止,我能想到的唯一解决方案是蛮力-将每对水平段(O(N^2)
)与每对垂直段(O(N^2)
)一起检查是否为O(N^4)
运行。显然,我们可以通过预先计算哪些段可以放在一起来优化此过程,但这仍将时间复杂度保持在O(N^4)
。
理想情况下,我正在寻找O(N^2)
的解决方案,但是如果您的内容少于O(N^4)
,请分享!
答案 0 :(得分:2)
您提供的示例:
实际上,一旦我们提取并合并仅由交点形成的矩形,就可以简化为以下形式:
---------------------
| |
| |
| |
| |
--------- ------------------
| |
|____________________________|
然后问题就变成了在一个直线(也称为正交)多边形中找到最大的矩形,为此,有大量文献。
答案 1 :(得分:2)
在这种情况下,垂直线是从一组线中添加或删除的,以在向上移动时加以考虑。起点和终点o线都添加到扫描集中,水平线也添加到列表中。
等
代码(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++;
}
}
}
}
}
答案 2 :(得分:1)
您可以通过扫描找到垂直线和水平线之间的所有交点。按y递增的顺序遍历所有行。维护一个包含所有垂直线(包括y的当前值)的缓冲区。使缓冲区按每个垂直线的x值排序。当您到达每条水平线时,请检查其是否与缓冲区中的任何线相交。最糟糕的情况是,当有O(N ^ 2)个交叉点时。
现在,您有了一个交点列表,以及每条线的交点列表。对于每个水平线,对于每个交叉点,我们都会感兴趣,您可以沿着该交叉点的垂直线向下走多远。将这些值存储在数组中。将这些值分成几对,并将每对的最大值存储在数组中。为每个最大值重复该过程,依此类推。这将构建一个值树,其中叶子是原始值(按原始顺序),并且每个节点都携带任何后代中的最大值。交叉点数量的总成本是线性的。
现在,将每个交叉点都假定为矩形的左下角。对于在其垂直线上的每个交叉点,请查看相交的水平线,并在此线上找到最右边的点,在该点上您至少可以下降到该交叉点。您已经建立了一棵树,使您可以在那条线上的交叉点数量上按时间对数找到该树:从树的顶部开始,如果该子项的值至少与您需要的走数一样远,则向右走,否则往左走。找到此位置后,您将使用该左下角和该水平线为您提供最大的矩形,因此,对每条水平线进行检查都会得到最大的矩形,其中包括该交点为左下角,并针对每个相交重复此操作,您将获得整体上最大的矩形。>
如果线形成一个N x N网格,那么对于每个交叉点,您都要以O(log N)的代价检查其上方的O(N)条水平线,因此此阶段的总成本为O(N ^ 3log(N))在最坏的情况下。