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 }
独立意志与自由思想是必须争的,且须以生死力争。