1. 程式人生 > >CodeForces - 1059D——二分/三分

CodeForces - 1059D——二分/三分

題目

題目連結

簡單的說,就是作一個圓包含所有的點且與x軸相切,求圓的最小半徑

方法一

分析:求最小,對半徑而言肯定滿足單調性,很容易想到二分。我們二分半徑,然後由於固定了與X軸相切,我們對於每一個點,就可以算出這個點在圓上的時候圓與x軸相交的距離(其實就是圓心的x軸的範圍)。然後對每個點都可以求一個圓心的橫座標區間,如果所有的區間有相交區域,則該半徑滿足條件,否則不滿足。

程式碼:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<algorithm>
 5
using 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) return
false; //大於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