Luogu P1663 山【二分答案/实数域】By cellur925

题目传送门

现在要在山上的某个部位装一盏灯,使得这座山的任何一个部位都能够被看到。

给出最小的y坐标,如图的+号处就是y坐标最小的安装灯的地方。

这个题嘛...今年省选前学姐来我们(破烂)的机房串门的时候提到了这个题qwq学姐表示十分毒瘤qwq

压了很久今天终于做了qwq

因为问题说的太模糊了233,所以我们首先需要简化一下题意。(开始在如何判断能看到灯的问题上卡了很久)

题目其实说的是:把给出的(相邻的)拐点连成直线,找到一个点的纵坐标,使这个点在所有的直线上方或恰好在直线上。

仔细考虑一下这个其实是有二分单调性的,我们便可以二分要求的纵坐标。

首先我们把所有直线的信息求出(斜率、截距,详见必修2qwq)。

然后在二分答案的判定中,我们可以确定以二分出的答案为坐标的点在各直线上的横坐标。确定横坐标的范围,若横坐标范围是个合法的区间,我们就可以判定有解。

根据必修2的学习,我们知道斜率是一个易错点(逃),判定的时候需要分类讨论斜率大于0小于0等于0的情况(等于0很重要,防止整数被0除)

因为平时一直在做整数域上的二分,所以这次(恰巧打了一下实数域上的二分),这时需要尤其注意精度问题。

实数域二分例 eps用来控制精度,通常可取为1e-8

1 while(l+eps<r)
2 {
3     double mid=(l+r)/2;
4     if(check(mid)) r=mid;
5     else l=mid;
6 }

而且在算直线信息的时候还要乘上那个1.0,在强制类型转换。

Code

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cmath>
 4 #include<utility>
 5 
 6 using namespace std;
 7 const double eps=1e-8;
 8 
 9 int n;
10 pair<int,int>p[6000];
11 struct line{
12     double k,b;
13 }li[6000];
14 
15 bool check(double x)
16 {
17     double l=-1000000,r=1000000;
18     for(int i=1;i<n;i++)
19     {
20         if(li[i].k<0) l=max(l,(x-li[i].b)/li[i].k); 
21         else if(li[i].k>0) r=min(r,(x-li[i].b)/li[i].k);
22         else if(li[i].k==0&&li[i].b>x) return 0; 
23     }
24     return l<=r;
25 }
26 
27 int main()
28 {
29     scanf("%d",&n);
30     for(int i=1;i<=n;i++) scanf("%d%d",&p[i].first,&p[i].second); 
31     for(int i=1;i<n;i++)
32     {
33         li[i].k=1.0*(p[i+1].second-p[i].second)/(p[i+1].first-p[i].first);
34         li[i].b=1.0*p[i].second-li[i].k*p[i].first;
35     }
36     double l=0,r=100000000;
37     while(l+eps<r)
38     {
39         double mid=(l+r)/2;
40         if(check(mid)) r=mid;
41         else l=mid;
42     }
43     printf("%.2lf",l);
44     return 0;
45 }
View Code
原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9717412.html