[蓝桥杯] 历届试题 荒岛探测
这个题目的测试点是有点问题的,毕竟不是第一次遇上这种情况了,个人感觉下面的代码应该可以过,如果真有问题,希望看见的可以在评论区告诉我一下,感激不尽。下面代码过了70%,又是这个数,第三次了,写的三篇蓝桥的全是70%的分,嗯。。。。这数字有毒
题目描述:
科学家小蓝来到了一个荒岛,准备对这个荒岛进行探测考察。
小蓝使用了一个超声定位设备来对自己进行定位。为了使用这个设备,小蓝需要在不同的点分别安装一个固定的发射器和一个固定的接收器。小蓝手中还有一个移动设备。定位设备需要从发射器发射一个信号到移动设备,移动设备收到后马上转发,最后由接收器接收,根据这些设备之间传递的时间差就能计算出移动设备距离发射器和接收器的两个距离,从而实现定位。
小蓝在两个位置已经安装了发射器和接收器,其中发射器安装在坐标( x A x_A xA, y A y_A yA) ,接收器安装在坐标( x B x_B xB, y B y_B yB)。小蓝的发射器和接收器可能在岛上,也可能不在岛上。
小蓝的定位设备设计有些缺陷,当发射器到移动设备的距离加上移动设备到接收器的距离之和大于L 时,定位设备工作不正常。当和小于等于L时,定位设备工作正常。为了安全,小蓝只在定位设备工作正常的区域探测考察。
已知荒岛是一个三角形,三个顶点的坐标分别为 ( x 1 x_1 x1, y 1 y_1 y1),( x 2 x_2 x2, y 2 y_2 y2) ,( x 3 x_3 x3, y 3 y_3 y3) 。
请计算,小蓝在荒岛上可以探测到的面积有多大?
样例输入
10 6 4 12 12
0 2 13 2 13 15
输出样例
39.99
解题思路
因为前不久刚好做了一道扫描线的题,所有比较容易想到用扫描线来做。题目的目的是求椭圆与三角行的相交部分的面积,解题大致思路就是用一条平行于y轴的直线设为xi,每次xi+0.001(本来估算+0.01的,不过测试时发现+0.001运行时间也是0毫秒,就干脆提高一点精度了),然后像积分那样将面积分成一个一个小矩形一点一点加起来。计算三角形和椭圆重叠部分面积等价于计算,所有相同xi下三角形里分割出的矩形与椭圆里分割出的矩形重叠面积之和如下图
所有x1下像图中椭圆浅粉色矩形与三角形灰蓝色重叠部分之和,就是我们要的答案了。
在有解情况下x=xi时,三角形与直线x=xi可以得到两个解y1,y2(y1<=y2),椭圆同理可得y3,y4(y3<=y4),如果min(y2,y4)>max(y1,y3) ,总面积sum就加上(min(y2,y4)-max(y1,y3) )* 0.001 (0.001是所选的精度)。三角形的y1,y2,计算比较简单,算三条直线即可。椭圆就比较麻烦了,毕竟高中只学过焦点所在直线在坐标轴上焦点的中点在坐标原点的,我先试了一下直接解方程
(xi- x A x_A xA)2+(y- y A y_A yA)2)1/2+((xi- x B x_B xB)2+(y- y B y_B yB)2)1/2=L
刚开始以为解这个方程很难就没有去算。。。写着写着博客感觉这就是两个平方的事。。。 觉得下面方法不合适自己的话可以尝试解方程,下面我讲一讲另一种方法:
由于只学过上述的那种椭圆,所以我的想法就是把二维坐标移动到自己想要的位置即可,即以椭圆中心为原点,焦点所在直线为x轴,其他的坐标同时更新,则椭圆方程为x2/a2+y2/b2=1。然后用椭圆准线(x=+a2/c,x=-a2/c)的性质(椭圆上的点到准线的距离比上该点到对应焦点的距离等于离心率a/c) ,再用上勾股定理就可以求对应的y值
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e5+5;
struct point{
double x,y;
bool operator<(const point& a){
return x<a.x;
}
point operator-(const point& a){
point k;
k.x=x-a.x;k.y=y-a.y;
return k;
}
point(){x=0,y=0;}
};
point P1,P2,R[3];
double A[3],B[3],a,b,c;
void Rjd(int a,int b,point &m1,point &m2,double x){//求三角形的y1,y2
if(fabs(B[a])<1e-6||fabs(B[b])<1e-6){
m1.y=min(R[a].y,R[b].y);
m2.y=max(R[a].y,R[b].y);
return;
}
m1.y=(1-A[a]*x)/B[a];
m2.y=(1-A[b]*x)/B[b];
if(m1.y>m2.y)swap(m1,m2);
}
void TY(double x,point &m1,point &m2){//横坐标为x时,求y1,y2 (x+a^2/c)/p=a/c p是横坐标为x的扫描线与椭圆的交点到椭圆左焦点的距离 y2=sqrt(p^2-(x+c)^2)
double p=(x*c/a+a);
m2.y=sqrt(p*p-(x+c)*(x+c));
m1.y=-m2.y;
}
int main(){ //测试数据有问题比如测试点五,用CAD作图得到的答案2279.7457,测试给的答案是2486.14,我的答案与作图答案一致。故只过70%,剩下两个测试点还未验证应该也是答案问题
int L;
cin>>P1.x>>P1.y>>P2.x>>P2.y>>L;
cin>>R[0].x>>R[0].y>>R[1].x>>R[1].y>>R[2].x>>R[2].y;//处理,重建坐标系,使焦点在x轴
if(P1.x>P2.x)swap(P1,P2);
c=(P1.x-P2.x)*(P1.x-P2.x)+(P1.y-P2.y)*(P1.y-P2.y);
c=sqrt(c)/2;//半焦距
a=L/2;
b=sqrt(a*a-c*c);// x^2/a^2+y^2/b^2=1
double tA1,tB1,tC1=1;//横坐标直线 tA1*x+tB1*y=1
tA1=(P2.y-P1.y)/(P1.x*P2.y-P1.y*P2.x);
tB1=(P1.x-P2.x)/(P1.x*P2.y-P1.y*P2.x);
point O;//新原点
O.x=(P1.x+P2.x)/2;O.y=(P1.y+P2.y)/2;
for(int i=0;i<3;i++)R[i]=R[i]-O;//更新三角形顶点相对位置
P2=P2-O;
double dx=-P2.y;
double sina=dx/c;//坐标轴逆时针旋转a° 新坐标为 x=Xcosa-Ysina y=Xsina+Ycosa
double cosa=sqrt(c*c-dx*dx)/c;
P1.x=-c;P1.y=0;
P2.x=c;P2.y=0;
for(int i=0;i<3;i++){
point temp=R[i];
R[i].x=temp.x*cosa-temp.y*sina;
R[i].y=temp.x*sina+temp.y*cosa;
}//所有点更新完毕
sort(R,R+3);
A[2]=(R[2].y-R[1].y)/(R[1].x*R[2].y-R[1].y*R[2].x);//直线方程A*x+B*y=1;
if(fabs(R[1].x-R[2].x)<1e-6)B[2]=0;
else B[2]=(R[1].x-R[2].x)/(R[1].x*R[2].y-R[1].y*R[2].x);
A[1]=(R[2].y-R[0].y)/(R[0].x*R[2].y-R[0].y*R[2].x);
if(fabs(R[2].x-R[0].x)<1e-6)B[1]=0;
else B[1]=(R[0].x-R[2].x)/(R[0].x*R[2].y-R[0].y*R[2].x);
A[0]=(R[0].y-R[1].y)/(R[1].x*R[0].y-R[1].y*R[0].x);
if(fabs(R[1].x-R[0].x)<1e-6)B[0]=0;
else B[0]=(R[1].x-R[0].x)/(R[1].x*R[0].y-R[1].y*R[0].x);
double sum=0;
double l=max(R[0].x,-a),r=min(R[2].x,a);
for(double i=l;i<r;i+=0.001){
point m1,m2,m3,m4;
if(i<R[1].x){
Rjd(0,1,m1,m2,i);
}else{
Rjd(2,1,m1,m2,i);
}
TY(i,m3,m4);
if(m1.y>m3.y)m3.y=m1.y;
if(m2.y<m4.y)m4.y=m2.y;
if(m3.y<m4.y)sum+=(m4.y-m3.y)*0.001;//面积重叠部分相加
}
printf("%.2lf\n",sum);
return 0;
}
以下是在autoCAD上画的图:
测试点三:
没有改变坐标轴时
改变坐标轴时:
测试样例五:
没改坐标轴时:
计算面积得2279.7457 而测试答案给的是2486.14,差的有点多,我的运行结果是2279.75,所以感觉应该是答案有问题
改坐标轴后: