51 nod: 1298 圆与三角形

题目

给出圆的圆心和半径,以及三角形的三个顶点,问圆同三角形是否相交。相交输出”Yes”,否则输出”No”。(三角形的面积大于0)。
这里写图片描述
这里写图片描述

Input

第1行:一个数T,表示输入的测试数量(1 <= T <= 10000),之后每4行用来描述一组测试数据。
4-1:三个数,前两个数为圆心的坐标xc, yc,第3个数为圆的半径R。(-3000 <= xc, yc <= 3000, 1 <= R <= 3000)
4-2:2个数,三角形第1个点的坐标。
4-3:2个数,三角形第2个点的坐标。
4-4:2个数,三角形第3个点的坐标。(-3000 <= xi, yi <= 3000)

Output

共T行,对于每组输入数据,相交输出”Yes”,否则输出”No”。

Input示例

2
0 0 10
10 0
15 0
15 5
0 0 10
0 0
5 0
5 5

Output示例

Yes
No

题解

判断一个圆是否与三角形相交,先对点的分布进行如下的判断:
1. 三个点都在圆内,不相交
2. 三个点都在圆外,需要特殊判断
3. 其它:相交

对于情况2:
1. 如果圆与任意一条边相交,则相交,对于每条边转入2
2. 对于每一条边,求点到直线距离,如果距离大于半径,无交点,否则,转入3
3. 如果以两个三角形点的为中心的角都是锐角,则相交,否则不相交,具体分析如下:

圆心到线段所在直线距离大于r
这种情况下,直接可以确定该线段与圆无交点
这里写图片描述

圆心到线段所在直线距离小于r
第一种情况:
这里写图片描述
这种情况下,我们可以得到, OAB=OCA+COA ,由于是垂足,因此 OCA=90 OAB>90 。因此得到对应的向量点乘后小于0。注:不需要考虑A是切点,如果是切点,就已经是前面的第3种情况,直接就判断相交。

第二种情况:
这里写图片描述
这种情况下,可以得到 OAC+AOC=90 ,因此 OAC<90 。同理分析 OBC 。因此这两个角都必须是锐角,即向量点乘后大于0。

直线方程
直线方程的标准式是: Ax+By+C=0 。需要对垂直和水平的直线进行特殊的考虑。点到直线的距离的公式是:

|Ax+By+C|A2+B2

代码

#include <iostream>
#include <limits>
#include <algorithm>

using namespace std;
using ll = long long;

ll Distance(ll x1, ll y1, ll x2, ll y2)
{
    return (x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2);
}

bool isSegmentCircle(ll x, ll y, ll r,ll px1, ll py1, ll px2, ll py2)
{
    ll a, b, c;     // 直线方程的三个参数
    if (px1 == px2)
    {
        a = 1;
        b = 0;
        c = -px1;
    }
    else if (py1 == py2)
    {
        a = 0;
        b = 1;
        c = -py1;
    }
    else
    {
        a = py2 - py1;
        b = px1 - px2;
        c = px2*py1 - px1*py2;
    }
    ll dMax = r*r*(a*a + b*b);
    ll dUp = a*x + b*y + c;
    dUp = dUp*dUp;      // 点到直线距离
    if (dUp > dMax)
        return false;
    // 向量点乘
    ll sita1 = (x - px1)*(px2 - px1) + (y - py1)*(py2 - py1);
    ll sita2 = (x - px2)*(px1 - px2) + (y - py2)*(py1 - py2);
    if (sita1 > 0 && sita2 > 0)
        return true;
    return false;
}

bool isInterset(ll x, ll y, ll r, ll posX[], ll posY[])
{
    ll d1 = Distance(x, y, posX[0], posY[0]);
    ll d2 = Distance(x, y, posX[1], posY[1]);
    ll d3 = Distance(x, y, posX[2], posY[2]);
    ll d = r*r;
    if (d1 < d && d2 < d && d3 < d)
        return false;
    if (d1 > d && d2 > d && d3>d)
    {
        bool f = false;     
        f = f || isSegmentCircle(x, y,r, posX[0], posY[0], posX[1], posY[1]);
        f = f || isSegmentCircle(x, y,r, posX[1], posY[1], posX[2], posY[2]);
        f = f || isSegmentCircle(x, y,r, posX[2], posY[2], posX[0], posY[0]);
        return f;
    }
    return true;
}

int main()
{
//  freopen("input.txt", "r", stdin);
    int T;
    cin >> T;
    ll x, y, r;
    ll posX[3];
    ll posY[3];
    while (T--)
    {
        scanf("%lld%lld%lld", &x, &y, &r);
        for (int i = 0; i < 3; ++i)
            scanf("%lld%lld", &posX[i], &posY[i]);
        if (isInterset(x, y, r, posX, posY))
            cout << "Yes\n";
        else
            cout << "No\n";
    }
    return 0;
}

思路参考自 http://blog.csdn.net/f_zyj/article/details/52066901

猜你喜欢

转载自blog.csdn.net/hu694028833/article/details/78723935