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

猜你喜欢

转载自www.cnblogs.com/lfri/p/9944289.html