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=90OAB>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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章