题目:雪色光晕
(自己当错题记录的,题解是我转载的,文末有原文链接,侵权我就删`(*>﹏<*)′)
知识点:计算几何
本题要求一个固定的点到一条折线的最短距离。由于折线可以看成是很多条线段,所以可以规约成点到线段的最短距离。
显然,最终的答案一定为以下两种情况的一种:点到直线的最短距离、或点到线段某个端点的最短距离。
什么时候会遇到第二种情况呢?当且仅当点到直线的最短距离所对应的那个点不在线段上。
所以这道题一个最笨的方法就是:先根据线段求出直线两点式,然后求斜率乘积为-1(这样才能垂直)的直线,求出点斜式(还要特判斜率不存在的情况),这样求出两个直线交点,判断交点在不在线段上。如果在的话直接输出初始点到交点距离,否则输出初始点到线段两个端点的最小那个距离。
如果这道题用这种方法来做,计算量将非常大,题目难度也直接飙上1700+。事实上,本题有更简单的做法:
首先如何判断最终能不能取到点到直线距离呢?很简单,把点 PPP 和线段两个端点 AAA 和 BBB ,这三个点连接成一个三角形,判断该三角形的角 AAA 和角 BBB 是否是钝角即可。判断钝角可以直接用勾股定理:a2+b2<c2a^2+b^2<c^2a2+b2<c2 则角 CCC 为钝角。
然后如何求点到直线的距离呢?也很简单,PPP到直线ABABAB 的距离即三角形 PABPABPAB 中ABABAB边上的高。直接用 2∗SABC/AB2*S_{ABC}/AB2∗SABC/AB即可。而三角形面积可以直接用海伦公式:S=p∗(p−a)∗(p−b)∗(p−c),p=(a+b+c)/2S=\sqrt{p*(p-a)*(p-b)*(p-c)},p=(a+b+c)/2S=p∗(p−a)∗(p−b)∗(p−c),p=(a+b+c)/2。
可以发现,换一个做法,原本计算量巨大的题目将极大的减少做题的负担。在赛场上如果发现某题计算量非常大,不妨洗个脸冷静一下,换一个思路可能会豁然开朗。
*请注意,本题如果直接用网上的long long 存点的板子,可能会导致答案错误。因为在计算过程中会出现超过10^9的情况,这样在计算勾股定理的时候再一个平方就会出现爆long long精度的问题。解决办法要么使用__int128或者高精度,要么直接用double存点。
#include<bits/stdc++.h>
using namespace std;
struct point{
double x,y;
point(double x,double y):x(x),y(y){}
};
double dis(point a,point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double ar(point A,point B,point C){ //三点三角形面积
double a=dis(B,C),b=dis(A,C),c=dis(A,B);
double p=(a+b+c)/2;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
int jud(point A,point B,point C){ //判断角ABC是钝角
double a=dis(B,C),b=dis(A,C),c=dis(A,B);
return b*b>a*a+c*c;
}
double f(point a,point b,point c){ //c点到ab线段的最小距离
double d1=dis(a,c),d2=dis(b,c);
if(jud(c,a,b)||jud(c,b,a))
return min(d1,d2);
double s=ar(a,b,c);
double d3=2*s/dis(a,b);
return min(min(d1,d2),d3);
}
int main(){
int n,i,j,k;
double x,y,x0,y0;
cin>>n;
cin>>x0>>y0>>x>>y;
point purple(x,y);
point red(x0,y0);
double res=1e16;
for(i=0;i<n;i++){
double xt,yt;
cin>>xt>>yt;
point temp(red.x+xt,red.y+yt);
res=min(res,f(red,temp,purple));
red=temp;
}
printf("%.8f",res);
}
作者:比那名居的桃子
链接:【题解】2022年第四场寒假集训营题解_ACM竞赛_ACM/CSP/ICPC/CCPC/比赛经验/题解/资讯_牛客竞赛OJ_牛客网
来源:牛客网