线段相交问题: 线段扫描法
预处理:
对每个线段, 解析其端点信息.
线段分两类, 一类与x轴平行, 作为扫描线段, 左端点记为LEFT, 右端点记为RIGHT
第二类与y轴平行, 作为区间查询线段, 上端点记为TOP, 下端点记为BOTTOM
对每个端点, 其类型为EndPoint, 除了存储坐标外, 还存储其线段下标以及端点类型
核心步骤:
给线段的端点排序, 按坐标y从小到大的顺序, 如果相同, 则按照标记从小到大(bottom, left, right, top依次递增)
维护一个set集合, 记录那些与y轴平行的线段的底端点的x坐标(每遇到就插入). 当遇到这些线段的顶端点时, 将该坐标弹出.
遇到一个与x轴平行线段的左端点时, 进行线段扫描. 右端点不进行处理
在set集合中的这些端点代表着当前y区间长度可访问到的与y轴平行的线段, 但扫描线段要与它们相交, 还需要判断区间长度, 用set内置函数可解决.
代码:
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
class Point {
public:
Point(int x = 0, int y = 0): x(x), y(y) {}
int x, y;
};
class Segment {
public:
Segment() {}
Segment(Point p1, Point p2): p1(p1), p2(p2) {}
Point p1, p2;
};
class EndPoint {
public:
Point p;
int seg, st;
EndPoint() {}
EndPoint(Point p, int seg, int st): p(p), seg(seg), st(st) {}
bool operator < (const EndPoint &ep) const {
if (p.y == ep.p.y) {
return st < ep.st;
} else return p.y < ep.p.y;
}
};
EndPoint EP[2 * 1000000];
#define BOTTOM 0
#define LEFT 1
#define RIGHT 2
#define TOP 3
int manhattanIntersection(vector<Segment> S)
{
int n = S.size();
for (int i = 0, k = 0; i < n; ++i) {
if (S[i].p1.y == S[i].p2.y) {
if (S[i].p1.x > S[i].p2.x) swap(S[i].p1, S[i].p2);
} else {
if (S[i].p1.y > S[i].p2.y) swap(S[i].p1, S[i].p2);
}
if (S[i].p1.y == S[i].p2.y) {
EP[k++] = EndPoint(S[i].p1, i, LEFT);
EP[k++] = EndPoint(S[i].p2, i, RIGHT);
} else {
EP[k++] = EndPoint(S[i].p1, i, BOTTOM);
EP[k++] = EndPoint(S[i].p2, i, TOP);
}
}
sort(EP, EP + 2*n);
set<int> BT;
int cnt = 0;
BT.insert(1000000001);
for (int i = 0; i < 2 * n; ++i) {
if (EP[i].st == TOP) {
BT.erase(EP[i].p.x);
} else if (EP[i].st == LEFT) {
set<int>::iterator start = BT.lower_bound(S[EP[i].seg].p1.x);
set<int>::iterator end = BT.upper_bound(S[EP[i].seg].p2.x);
cnt += distance(start, end);
} else if (EP[i].st == BOTTOM) {
BT.insert(EP[i].p.x);
}
}
return cnt;
}
int main()
{
int n;
cin >> n;
vector<Segment> seg;
for (int i = 0; i < n; ++i) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
seg.push_back(Segment(Point(x1, y1), Point(x2, y2)));
}
int ans = manhattanIntersection(seg);
cout << "这些线段共有 " << ans << " 个交点." << endl;
}
/*
6
2 2 2 5
1 3 5 3
4 1 4 4
5 2 7 2
6 1 6 3
6 5 6 7
*/