1. 程式人生 > >51nod1298--圓與三角形

51nod1298--圓與三角形

題目連結:戳一戳

題目描述:

解題思路:

題意很清楚了,給你三角形三個頂點,圓心,半徑。判斷是否相交。

我們先列出所有的情況來看下:

1、首先最明顯的,給你的這三個頂點如果存在某一個頂點就在圓上,那麼三角形和圓肯定是相交的了。(這個也很好判斷,把頂點座標帶入圓的方程,看是否等於 r*r 就好了)

2、如果所有的頂點都在圓內,三角形和圓肯定不相交。(判斷方法同上,看是否小於 r*r)

3、如果三個點 有在圓外的 也有在圓內的,那麼三角形和圓一定相交(判斷方法同上)

4、那麼就剩最後一種情況了,所有的點都在圓外。這種情況並不能直接確定是否相交,如下圖

那麼這種情況我們如何判斷是否相交呢?看圖大家也能觀察的出來。三條線段只要有一條和圓相交就說明三角形和圓相交了。

所以問題就轉化成了 “如何判斷線段是否和圓相交的問題”

那麼 我們學過距離公式可以求點到直線的距離  就像上圖的(1) 我們套公式求出圓心到三個直線的距離,與半徑一比較就知道相交不相交了。但是圖(2)的情況就不適用於這種方法了  如圖

我們求出的點到直線的距離 確實小於 r   但是三角形並不和圓相交  因為我們要判斷的是圓心到線段的距離

那麼怎麼做呢?如何判斷圓心是否於線段相交呢?

求出圓心到直線的距離  若大於R 那一定不相交 若距離小於R

再求出垂足的橫座標  若垂足的橫座標線上段兩個端點的橫座標之間,那麼相交,否則不相交。

 

到這裡 整個題目的思路做法已經出來了,剩下的就是實現的問題了

實現起來還有幾個小問題  是這個題需要注意的(沒錯 這才是坑點T_T)

首先先複習幾個公式

已知兩個點座標我們就能算出斜率  公式

得到了斜率k   我們就可以用     y=kx+b   (直線的點斜式方程)

還有一個性質  兩直線垂直 斜率之積等於 -1

1、如何求出垂足座標

由線段的兩個點我們可以求出線段所在直線的斜率,以及點斜式方程。然後垂足和圓心所在的直線是和他們垂直的,所以斜率之積等於-1。求得斜率,然後還知道圓心的座標,就可以求出點斜式方程。兩個直線的方程出來了,聯立就可以解出來交點的橫座標了。

2、斜率的問題

有三種情況很特殊,線段平行於x軸(斜率不存在),線段平行於y軸(斜率=0)。 這兩種情況,我們要先做特殊判斷。

當線段平行於x軸的時候。線段到圓心距離很好算,縱座標相減就行了(取絕對值)。那垂足在不線上段上,也很好判斷,看圓心的橫座標在不線上段兩個端點範圍內就行了。平行於y軸同理。

還有一種,線段所在的直線過圓心。這種情況我看到有的部落格上寫要特殊判斷,但是我的這個方法不需要特殊判斷。這種情況用我的方法,會算出圓心到直線的距離是0,也會解出垂足的橫座標(就是圓心的橫座標),然後和端點比較就行了。

3、比較相等的問題

這題輸入的資料是double  double是不能直接判斷相等的 需要相減判精度 注意一下

4、取絕對值問題

程式中很多地方判斷距離用座標相減,都需要做取絕對值操作。注意一下(因為這個wa了兩遍)

AC程式碼及詳細註釋:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
const int maxn=1007;
const int INF=0x3f3f3f3f;
const double decimal=0.00001;
pair<double,double> y,d1,d2,d3;
double r;
double dis(pair<double,double> a,pair<double,double> b){
    return (a.first-b.first)*(a.first-b.first)+(a.second-b.second)*(a.second-b.second);
}
bool Line_Round(pair<double,double> a,pair<double,double> b){
    //要先考慮直線平行於x軸或者y軸的情況 因為平行於x軸 斜率是不存在的
    //直線平行於x軸
    if(fabs(a.second-b.second)<decimal){
        if(fabs(a.second-y.second)>r)return false;
        if(y.first<=max(a.first,b.first)&&y.first>=min(a.first,b.first))return true;
        else return false;
    }
    //直線平行於y軸
    if(fabs(a.first-b.first)<decimal){
        if(fabs(a.first-y.first)>r)return false;
        if(y.second<=max(a.second,b.second)&&y.second>=min(a.second,b.second))return true;
        else return false;
    }
    //不平行與x和y
    double k=(b.second-a.second)/(b.first-a.first);//計算斜率
    double bb=a.second-k*a.first;//公式中的b
    double dd=fabs( (k*y.first-y.second+bb)/sqrt(k*k+1) );//圓心到直線距離
    if(dd>r)return false;//圓心到直線的距離大於r 線段與圓不相交
    //圓心到直線的距離小於等於r
    double k2=-1/k;
    double bb2=y.second-k2*y.first;
    double cz_x=(bb2-bb)/(k-k2);//計算垂足的橫座標
    if(cz_x<=max(a.first,b.first)&&cz_x>=min(a.first,b.first))return true;
    else return false;
}
bool check(){
    double a=dis(d1,y),b=dis(d2,y),c=dis(d3,y);

    //如果有點在圓上 那麼肯定相交
    if(fabs(a-r*r)<decimal||fabs(b-r*r)<decimal||fabs(c-r*r)<decimal)return true;

    //如果都在圓內 一定不相交
    if(a<r*r&&b<r*r&&c<r*r)return false;

    //如果圓內有點 圓外也有點 那麼一定相交
    bool n=false,w=false;
    if(a<r*r||b<r*r||c<r*r)n=true;
    if(a>r*r||b>r*r||c>r*r)w=true;
    if(n&&w)return true;
    //所有的點都在圓外
    if(Line_Round(d1,d2)||Line_Round(d1,d3)||Line_Round(d2,d3))return true;
    else return false;
}
int main(){
    int T; scanf("%d",&T);
    while(T--){
        scanf("%lf%lf%lf",&y.first,&y.second,&r);
        scanf("%lf%lf%lf%lf%lf%lf",&d1.first,&d1.second,&d2.first,&d2.second,&d3.first,&d3.second);
        if(check())printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}