圓的國度:Can you understand what you see?
題目描述:
平面上有 n 個沒有公共點 的圓。你要從點(x1,y1)走到(x2,y2)。問你最少要經過多少圓的邊界。保證這兩個點都不在圓的邊界上。
輸入格式:
問題輸入: 第一行一個整數 n, 1<=n<=50。 接下來三行每行 n 個整數,分別表示 n 個圓的圓心和半徑,格式如下: x1,x2……xi……xn , y1,y2……yi……yn , r1,r2……ri……rn -1000<=xi,yi<=1000,1<=ri<=1000 。最後一行四個整數 x1,y1,x2,y2, -1000<=x1,y1,x2,y2<=1000。
輸出格式:
問題輸出: 一個整數,意義如上。
樣例:
輸入: 輸入:
3 1
0 -6 6 0
0 1 6 0
2 2 2 2
-5 1 5 1 -5 1 5 1
輸出:
1 0
luogu連結:https://www.luogu.com.cn/problem/T139630
相信大家看到這道題的第一印象都是:
好 ~~一 ~~道 ~~水~~題 ~~
迴圈輸入這些玩意,把起點終點一連,寫個計數器,搞幾個判斷不就完了嗎,這題不是有手就行???
然而,事實真的如此嗎?
根據題意,我們很容易畫出樣例圖:
(樣例一)(請原諒我拙劣的畫圖技術)
WTF?!?!?!?!
這明明經過了2個圓的邊界,為啥子樣例輸出是1???
難道樣例有問題???
樣例二也有相同的問題,在圖上穿過了一個一個圓,而輸出卻是0。
經驗告訴我們樣例錯誤的情況極少出現,更何況一次錯兩個。出問題的一定是我們自己。
我們不妨重新審視一下題目。
注意【你要從點(x1,y1)走到(x2,y2)】,題目中僅僅說走到,卻絲毫未提及怎麼走。難道道路只有連線兩點的一條嗎?顯然並非如此。題目中沒有說必須走直線,翻譯過來就是:只要能到,你怎麼蛇皮走位都沒問題。
於是,樣例一便可以這麼走:
甚至這樣走:
這樣都滿足了從一點到另一點的要求,而且只穿過一個圓,符合樣例輸出。
結合上圖可知,與穿過幾個圓直接掛鉤的是起點和終點是否在圓內,若在,則會穿過一圓。
而點是否在圓內,只需要比較點到圓心的距離與該圓半徑,若距離>半徑,則點在圓外,若距離<半徑,則點在圓內(資料保證點不在圓上)。
故我們可以寫出程式:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; int n,x[60],y[60],r[60]; int s1,s2,s3,x01,y01,x02,y02; int main() { scanf("%d",&n); int count=0; for(int i=1;i<=n;i++) //注意輸入圓心和半徑要分開輸入 { scanf("%d",&x[i]); } for(int i=1;i<=n;i++) { scanf("%d",&y[i]); } for(int i=1;i<=n;i++) { scanf("%d",&r[i]); } scanf("%d%d%d%d",&x01,&y01,&x02,&y02); for(int i=1;i<=n;i++) { s1=sqrt((x01-x[i])*(x01-x[i])+(y01-y[i])*(y01-y[i])); //計算點到圓心距離 s2=sqrt((x02-x[i])*(x02-x[i])+(y02-y[i])*(y02-y[i])); if(s1<r[i]) count++; //判斷點到圓心距離和半徑哪個大 if(s2<r[i]) count++; } printf("%d",count); return 0; }
這段程式看上去沒什麼問題,兩個樣例也都能過,那就交吧!
然而…………
WA~~~~~~~~~~~~~~~~~~~~
最後一個測試點沒有過,說明還有問題。
遇到只錯了一個測試點的情況,我們該怎麼辦呢?
當然是卡測試點繼續debug。
我們上上方的程式中判斷計數是否加一,是通過比較點到圓心距離和圓的半徑,但是這種操作並不適用於一種較特殊的情況,如圖:
很明顯,這種情況下從A到B需要穿過的圓的數目為0,但若以我們的評測標準,因為A和B到圓心距離都小於圓的半徑,計數會加2,所以得出的答案是2,明顯錯誤。這就是兩點在同一圓內的特殊情況,需要另加判斷。
我們很容易發現,在這種情況下,兩個點到圓心的距離都小於半徑,而之前的情況是一個點到圓心距離小於半徑、一個大於半徑,於是,我們便可以由此寫出特判程式:
if(s1<r[i]&&s2<r[i]) continue; //使用continue直接跳過此情況。注意,要先判斷是否在同一圓內,否則計數會先加2 else if(s1<r[i]||s2<r[i]) count++;
到此這道題就全部結束了,AC程式碼:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; int n,x[60],y[60],r[60]; int s1,s2,s3,x01,y01,x02,y02; int main() { scanf("%d",&n); int count=0; for(int i=1;i<=n;i++) { scanf("%d",&x[i]); } for(int i=1;i<=n;i++) { scanf("%d",&y[i]); } for(int i=1;i<=n;i++) { scanf("%d",&r[i]); } scanf("%d%d%d%d",&x01,&y01,&x02,&y02); for(int i=1;i<=n;i++) { s1=sqrt((x01-x[i])*(x01-x[i])+(y01-y[i])*(y01-y[i])); s2=sqrt((x02-x[i])*(x02-x[i])+(y02-y[i])*(y02-y[i])); if(s1<r[i]&&s2<r[i]) continue; else if(s1<r[i]||s2<r[i]) count++; } printf("%d",count); return 0; }
小蒟蒻的此篇題解就到此結束了,喜歡的話還請留下一個大拇指。