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;
}