CodeForces - 1059D——二分/三分
題目
簡單的說,就是作一個圓包含所有的點且與x軸相切,求圓的最小半徑
方法一
分析:求最小,對半徑而言肯定滿足單調性,很容易想到二分。我們二分半徑,然後由於固定了與X軸相切,我們對於每一個點,就可以算出這個點在圓上的時候圓與x軸相交的距離(其實就是圓心的x軸的範圍)。然後對每個點都可以求一個圓心的橫座標區間,如果所有的區間有相交區域,則該半徑滿足條件,否則不滿足。
程式碼:
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5using namespace std; 6 7 const int maxn = 100000 + 10; 8 const double esp = 1e-6; 9 struct Piont 10 { 11 double x, y; 12 }p[maxn]; 13 int n; 14 15 bool judge(double r) 16 { 17 double ml = -1e15, mr = 1e15; 18 for (int i = 0; i < n; i++) 19 { 20 if (p[i].y > 2 * r) returnfalse; //大於2倍半徑的,肯定不行 21 double tmp = sqrt(2 * p[i].y * r - p[i].y * p[i].y); 22 if (p[i].x - tmp > ml) ml = p[i].x - tmp; 23 if (p[i].x + tmp < mr) mr = p[i].x + tmp; 24 } 25 return ml <= mr; //所有區間必須要有交點 26 } 27 28 int main() 29 { 30 while(scanf("%d", &n) == 1) 31 { 32 int flag1 = 0, flag2 = 0; 33 for (int i = 0; i < n; i++) 34 { 35 scanf("%lf%lf", &p[i].x, &p[i].y); 36 if (p[i].y > 0) flag1 = 1; 37 if (p[i].y < 0) flag2 = 1; 38 if (p[i].y < 0) p[i].y = -p[i].y; 39 } 40 if (flag1 && flag2) 41 { 42 printf("-1\n"); 43 continue; 44 } 45 46 double l = 0, r = 1e15, mid; //直接列舉圓的半徑,範圍大約是1e7的平方 47 int cnt = 100; //二分100次精度足夠了 48 while (cnt--) //這裡寫成(r - l) < esp陷入了死迴圈。。。 49 { 50 mid = (r + l) / 2.0; 51 if (judge(mid)) r = mid; 52 else l = mid; 53 } 54 printf("%.10lf\n", r); 55 } 56 return 0; 57 }
方法二
分析:設圓心的橫座標為x,由勾股定理有(x-x0)2 + (r-y)2 = r2,得r = (x-x0)2/2y + y/2,所以R = max(r1,r2,,,rn),也就是說x確定時,R也隨之確定。
我們又發現,對於答案所在得X,在它左右走R都會單調遞增,形成像山谷那樣得形狀,那麼直接三分X直接找到谷底即可。
具體的三分做法如下:
設答案所在區間為(l,r),dx = (r-l)/3,則mr = r+dx,ml = l-dx。設cal(x)是計算圓心在x時r的值,若cal(ml) < cal(mr),有兩種情況,異側如①,同側如③,所以將r更新為mr,而不是更新為ml,同理,若cal(ml) > cal(mr),則將l更新為ml。
程式碼:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 const int maxn = 100000 + 10; 7 const double esp = 1e-6; 8 struct Piont 9 { 10 double x, y; 11 }p[maxn]; 12 int n; 13 14 double cal(double x) 15 { 16 double r = 0; 17 for (int i = 0; i < n; i++) 18 r = max(r, p[i].y / 2.0 + (x - p[i].x) * (x - p[i].x) / p[i].y / 2.0); 19 return r; 20 } 21 22 int main() 23 { 24 while (scanf("%d",&n) == 1) 25 { 26 int flag1 = 0, flag2 = 0; 27 for (int i = 0; i < n; i++) 28 { 29 scanf("%lf%lf", &p[i].x, &p[i].y); 30 if (p[i].y > 0) flag1 = 1; 31 if (p[i].y < 0) flag2 = 1; 32 if (p[i].y < 0) p[i].y = -p[i].y; 33 } 34 if (flag1 && flag2) 35 { 36 printf("-1\n"); 37 continue; 38 } 39 //R=y1/2 + (x-x1)^2/2y,x是圓心座標,R關於x先減後增 40 double l = -1e7, r = 1e7,dx; //列舉圓的半徑,也就是列舉圓心橫座標 41 while (r - l > esp) //也可改成cnt<100 42 { 43 dx = (r - l) / 3.0; 44 double lx = l + dx, rx = r - dx; 45 if (cal(lx) - cal(rx) < 0) r = rx; 46 else l = lx; 47 } 48 int tmp = cal(r); 49 printf("%.6lf\n", cal(r)); 50 } 51 return 0; 52 }
參考連結:
https://blog.csdn.net/lzc504603913/article/details/82949923
https://blog.csdn.net/qq_37555704/article/details/82949337
http://www.cnblogs.com/sdfzhsz/p/9748360.html
https://blog.csdn.net/winter2121/article/details/82949159?tdsourcetag=s_pctim_aiomsg