计算几何两题

判断直线是否相交和判断线段是否相交似乎是高频面试题,总结一下这两道题目的解法。

首先,二维向量内积 v1 · v2 = x1 * x2 + y1 * y2。二维向量外积 v1×v2 = x1*y2 - x2*y1。

1. 直线是否平行或垂直

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A

Parallel/Orthogonal

 

For given two lines s1 and s2, print "2" if they are parallel, "1" if they are orthogonal, or "0" otherwise.

s1 crosses points p0 and p1, and s2 crosses points p2 and p3.

Input

The entire input looks like:

q (the number of queries)
1st query
2nd query
...
qth query

Each query consists of integer coordinates of the points p0, p1, p2, p3 in the following format:

xp0 yp0 xp1 yp1 xp2 yp2 xp3 yp3

Output

For each query, print "2", "1" or "0".

Constraints

  • 1 ≤ q ≤ 1000
  • -10000 ≤ xpiypi ≤ 10000
  • p0≠p1 and p2≠p3.

Sample Input 1

3
0 0 3 0 0 2 3 2
0 0 3 0 1 1 1 4
0 0 3 0 1 1 2 2

Sample Output 1

2
1
0

比较容易,首先得到两个直线的方向向量 v1 = (x0-x1, y0-y1), v2 = (x2-x3, y2-y3)。垂直则 v1·v2 = 0;平行则 v1×v2 = 0。理论上还应该在平行的判断里加上两条直线是否是同一条直线(三点共线),不过这一题答案似乎把共线也算到平行里了。

#include 
using namespace std;

bool isParallel(pair& a, pair& b)
{
    return a.first * b.second - a.second * b.first == 0;
}

bool isOthogonal(pair& a, pair& b)
{
    return a.first * b.first + a.second * b.second == 0;
}

int main(){
    int query = 0;
    scanf("%d", &query);
    for(; query > 0; query--)
    {
        int x0, y0, x1, y1, x2, y2, x3, y3;
        scanf("%d%d%d%d%d%d%d%d", &x0, &y0, &x1, &y1, &x2, &y2, &x3, &y3);
        //v1 = (x0-x1,y0-y1) v2 = (x2-x3,y2-y3)
        //平行: v1 x v2 = 0,需要判断 (x0-x2,y0-y2)与v1的叉积结果判断是否共线
        //垂直: v1 · v2 = 0
        pair v1 = {x0 - x1, y0 - y1};
        pair v2 = {x2 - x3, y2- y3};
        pair v3 = {x0 - x2, y0 - y2};
        if(isOthogonal(v1, v2)) printf("1\n");
        else if(isParallel(v1, v2)) printf("2\n");
        else printf("0\n");
    }
    return 0;
}

2. 线段是否相交

比较复杂,常见的做法是先进行快速排斥实验,判断二者是否可能相交。再进行跨立实验,判断二者是否真的相交。

快速排斥实验:

对于线段 (p0, p1) 和 (p2, p3),其相交的必要条件是:

min(p0.x, p1.x) <= max(p2.x, p3.x) && \
min(p2.x, p3.x) <= max(p0.x, p1.x) && \
min(p0.y, p1.y) <= max(p2.y, p3.y) && \
min(p2.y, p3.y) <= max(p0.y, p1.y);

跨立实验:

即判断 p0, p1两点是否跨立在线段p2p3两侧,以及 p2, p3两点是否跨立在线段p0p1两侧。

判断 p0, p1两点是否跨立在p2p3两侧,用外积, p2p3 × p2p0 应该与 p2p3 × p2p1异号。或者其中有0,说明有点重合,那么自然相交。另外一组同理。

即 (\overrightarrow{p2p3}\times \overrightarrow{p2p0})\, *(\overrightarrow{p2p3}\times \overrightarrow{p2p1})\, \leq0\: \&\&\: (\overrightarrow{p0p1}\times \overrightarrow{p0p2})\, *(\overrightarrow{p0p1}\times \overrightarrow{p0p3})\, \leq0

同时需要注意,乘法可能导致整型溢出,用double或long double,此时判断 <= 0 就改成 <= eps。

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_B

Intersection

 

For given two segments s1 and s2, print "1" if they are intersect, "0" otherwise.

s1 is formed by end points p0 and p1, and s2 is formed by end points p2 and p3.

Input

The entire input looks like:

q (the number of queries)
1st query
2nd query
...
qth query

Each query consists of integer coordinates of end points of s1 and s2 in the following format:

xp0 yp0 xp1 yp1 xp2 yp2 xp3 yp3

Output

For each query, print "1" or "0".

Constraints

  • 1 ≤ q ≤ 1000
  • -10000 ≤ xpiypi ≤ 10000
  • p0≠p1 and p2≠p3.

Sample Input 1

3
0 0 3 0 1 1 2 -1
0 0 3 0 3 1 3 -1
0 0 3 0 3 -2 5 0

Sample Output 1

1
1
0
#include 
using namespace std;

struct Point
{
    int x, y;
    Point(int x_ = 0, int y_ = 0):x(x_), y(y_){};
    Point operator - (const Point& other)
    {
        return Point(x - other.x, y - other.y);
    }
};

int cross(const Point& a, const Point& b)
{
    return a.x * b.y - a.y * b.x;
}

bool rapidRejection(const Point& p0, const Point& p1, const Point& p2, const Point& p3)
{
    return \
    min(p0.x, p1.x) <= max(p2.x, p3.x) && \
    min(p2.x, p3.x) <= max(p0.x, p1.x) && \
    min(p0.y, p1.y) <= max(p2.y, p3.y) && \
    min(p2.y, p3.y) <= max(p0.y, p1.y);
}

int main(){
    int query = 0;
    scanf("%d", &query);
    long double eps = 1e-10;
    for(; query > 0; query--)
    {
        Point p0, p1, p2, p3;
        scanf("%d%d%d%d%d%d%d%d", &p0.x, &p0.y, &p1.x, &p1.y, &p2.x, &p2.y, &p3.x, &p3.y);
        //快速排斥
        if(!rapidRejection(p0, p1, p2, p3))
        {
            printf("0\n");
            continue;
        }
        //p0 p1 是否跨立在线段p2p3两边: p2p3 x p2p0 与 p2p3 x p1p0 异号,或其中一个为0(说明三点共线)
        //p2 p3 是否跨立在线段p0p1两边
        if( (long double)cross(p2-p3, p2-p0) * cross(p2-p3, p2-p1) <= eps && (long double)cross(p0-p1, p0-p2) * cross(p0-p1, p0-p3) <= 0)
            printf("1\n");
        else printf("0\n");
    }
    return 0;
}

 

你可能感兴趣的:(计算几何两题)