📜  查找水平线段和垂直线段之间的三角形数量

📅  最后修改于: 2022-05-13 01:57:13.930000             🧑  作者: Mango

查找水平线段和垂直线段之间的三角形数量

先决条件: BIT给定'n'条线段,每条线段要么是水平的,要么是垂直的,求这些线段的交点连接起来可以形成的三角形(包括面积为零的三角形)的最大数目。

没有两条水平线段重叠,也没有两条垂直线段重叠。一条线用两个点表示(四个整数,前两个分别是第一个点的 x 和 y 坐标,另外两个分别是第二个点的 x 和 y 坐标)

例子:

|
    ---|-------|--
       |       |    -----
       |  --|--|-     |
       |       |      |

For the above line segments, there are four points of intersection between 
vertical and horizontal lines, every three out of which form a triangle, 
so there can be 4C3 triangles.

该想法基于扫描线算法。



分步构建解决方案:

  1. 将具有相应事件(如下所述)的所有线段的两个点存储在一个向量中,并按其 x 坐标的非递减顺序对所有点进行排序。
  2. 现在让我们想象一条垂直线,我们扫过所有这些点并根据我们当前所在的点描述 3 个事件:
    • in – 水平线段的最左边点
    • out – 水平线段的最右边点
    • 一条垂直线
  3. 我们称之为有过的第一个事件,但没有第二区“主动”或横线“活动”。我们将有一个 BIT(二进制索引树)来存储所有活动线的“y”坐标。
  4. 一旦一行变为非活动状态,我们就从 BIT 中删除它的“y”。
  5. 当第三类事件发生时,即当我们在一条垂直线上时,我们在它的 'y' 坐标范围内查询树,并将结果与到目前为止的交点数相加。
  6. 最后,我们将有交点的数量,比如m ,那么三角形(包括零面积)的数量将是m C 3

注意:我们需要仔细排序点,看实现中的cmp()函数进行澄清。

// A C++ implementation of the above idea
#include
#define maxy 1000005
#define maxn 10005
  
using namespace std;
  
// structure to store point
struct point
{
    int x, y;
    point(int a, int b)
    {
        x = a, y = b;
    }
};
  
// Note: Global arrays are initially zero
// array to store BIT and vector to store
// the points and their corresponding event number,
// in the second field of the pair
int bit[maxy];
vector > events;
  
// compare function to sort in order of non-decreasing
// x coordinate and if x coordinates are same then
// order on the basis of events on the points
bool cmp(pair &a, pair &b)
{
    if ( a.first.x != b.first.x )
        return a.first.x < b.first.x;
  
    //if the x coordinates are same
    else
    {
        // both points are of the same vertical line
        if (a.second == 3 && b.second == 3)
        {
            return true;
        }
  
        // if an 'in' event occurs before 'vertical'
        // line event for the same x coordinate
        else if (a.second == 1 && b.second == 3)
        {
            return true;
        }
  
        // if a 'vertical' line comes before an 'in'
        // event for the same x coordinate, swap them
        else if (a.second == 3 && b.second == 1)
        {
            return false;
        }
  
        // if an 'out' event occurs before a 'vertical'
        // line event for the same x coordinate, swap.
        else if (a.second == 2 && b.second == 3)
        {
            return false;
        }
  
        //in all other situations
        return true;
    }
}
  
// update(y, 1) inserts a horizontal line at y coordinate
// in an active region, while update(y, -1) removes it
void update(int idx, int val)
{
    while (idx < maxn)
    {
        bit[idx] += val;
        idx += idx & (-idx);
    }
}
  
// returns the number of lines in active region whose y
// coordinate is between 1 and idx
int query(int idx)
{
    int res = 0;
    while (idx > 0)
    {
        res += bit[idx];
        idx -= idx & (-idx);
    }
    return res;
}
  
// inserts a line segment
void insertLine(point a, point b)
{
    // if it is a horizontal line
    if (a.y == b.y)
    {
        int beg = min(a.x, b.x);
        int end = max(a.x, b.x);
  
        // the second field in the pair is the event number
        events.push_back(make_pair(point(beg, a.y), 1));
        events.push_back(make_pair(point(end, a.y), 2));
    }
  
    //if it is a vertical line
    else
    {
        int up = max(b.y, a.y);
        int low = min(b.y, a.y);
  
        //the second field of the pair is the event number
        events.push_back(make_pair(point(a.x, up), 3));
        events.push_back(make_pair(point(a.x, low), 3));
    }
}
  
// returns the number of intersection points between all
// the lines, vertical and horizontal, to be run after the
// points have been sorted using the cmp() function
int findIntersectionPoints()
{
    int intersection_pts = 0;
    for (int i = 0 ; i < events.size() ; i++)
    {
        //if the current point is on an 'in' event
        if (events[i].second == 1)
        {
            //insert the 'y' coordinate in the active region
            update(events[i].first.y, 1);
        }
  
        // if current point is on an 'out' event
        else if (events[i].second == 2)
        {
            // remove the 'y' coordinate from the active region
            update(events[i].first.y, -1);
        }
  
        // if the current point is on a 'vertical' line
        else
        {
            // find the range to be queried
            int low = events[i++].first.y;
            int up = events[i].first.y;
            intersection_pts += query(up) - query(low);
        }
    }
    return intersection_pts;
}
  
// returns (intersection_pts)C3
int findNumberOfTriangles()
{
    int pts = findIntersectionPoints();
    if ( pts >= 3 )
        return ( pts * (pts - 1) * (pts - 2) ) / 6;
    else
        return 0;
}
  
  
// driver code
int main()
{
    insertLine(point(2, 1), point(2, 9));
    insertLine(point(1, 7), point(6, 7));
    insertLine(point(5, 2), point(5, 8));
    insertLine(point(3, 4), point(6, 4));
    insertLine(point(4, 3), point(4, 5));
    insertLine(point(7, 6), point(9, 6));
    insertLine(point(8, 2), point(8, 5));
  
    // sort the points based on x coordinate
    // and event they are on
    sort(events.begin(), events.end(), cmp);
  
    cout << "Number of triangles are: " <<
         findNumberOfTriangles() << "\n";
  
    return 0;
}

输出:

Number of triangles are: 4
Time Complexity: O( n * log(n) + n * log(maximum_y) )