3200难度的几何题,AC后只有6个人通过 ?
题目
给出一个长方形区域,区域内有一些圆形的禁区,告知起点和终点,要求找一条从起点出发到终点的折线路径,要求避开禁区(可以贴着通过)
题解
初看这题,想着递归求解:假设两点的直线不能通过的话,就随机一个中转点,然后求解子问题
后来发现,太不靠谱… …
仔细看数据的要求,禁区两两相离,而且没有与边界相切的禁区,而且,人可以贴着边界走
于是发现,如果起点和终点都能走到边界,那么问题就解决了
进一步发现,都可以走到下边界
于是问题变成,给一点,找一条通向下边界的路径
简单了很多
一开始,我们就竖直向下走,没有障碍区的话,直接走到下边界,结束,否则有两种情况
第一种情况的话,走到交点后,把方向变为向下相切就好了,另一种情况对称,如图
接着走,如果能直达边界,结束,否则,有两种情况
第一种的话,到交点,变方向为相切,第二种的话,变方向为竖直向下
总结发现,这就是一个找交点,变方向的可循环的过程
找交点,用一个直线和圆相切的板子,但是要注意,实际是一个射线,要舍弃一些点,此外,有很多交点的话,要找距离最近的那个
变方向,交点在圆的下半部分,直接变为竖直向下,否则,分左右部分讨论就好了
WA on Test 39 ?
题目要求路径不能有重复的点,再处理一下就好了
代码
#include<bits/stdc++.h>
#define N 10010
#define INF 0x3f3f3f3f
#define eps 1e-9
#define pi 3.141592653589793
#define mod 998244353
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<" : "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y+3))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef pair<int,int> pp;
int dcmp(double x){if (fabs(x)<eps)return 0;else return x<0?-1:1;}
int n; double x1,x2,y1,y2;
struct Point{
double x,y;
Point(double x=0,double y=0):x(x),y(y){ }
void read(){scanf("%lf%lf",&x,&y);}
}st,en,a[55];
double r[55];
typedef Point Vector;
Vector operator + (Vector a,Vector b){return Vector(a.x+b.x,a.y+b.y);}
Vector operator - (Vector a,Vector b){return Vector(a.x-b.x,a.y-b.y);}
Vector operator * (Vector a,double b){return Vector(a.x*b,a.y*b);}
Vector operator / (Vector a,double b){return Vector(a.x/b,a.y/b);}
bool operator == (Vector a,Vector b){return dcmp(a.x-b.x)==0 && dcmp(a.y-b.y)==0;}
double Dot(Vector a,Vector b){return a.x*b.x+a.y*b.y;} //点积
double Length(Vector a){return sqrt(Dot(a,a));}
double Angle(Vector a,Vector b){return acos(min(1.0,max(-1.0,Dot(a,b)/Length(a)/Length(b))));}// 范围[0,180]
double Cross(Vector a,Vector b){return a.x*b.y-a.y*b.x;} //叉积
Vector Rotate(Vector a,double rad)// 向量 a 逆时针旋转 rad
{return Vector(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));}
struct Circle{
Point c;
double r;
Circle(Point c=Point(0,0), double r=0):c(c),r(r){}
};
//直线和圆的交点,返回交点个数,结果存在sol中。
int getLineCircleIntersecion(Point p,Point v, Circle C, vector<Point>& sol) {
sol.clear();
double a = v.x, b = p.x - C.c.x, c = v.y, d = p.y - C.c.y;
double e = a*a + c*c, f = 2*(a*b + c*d), g = b*b + d*d - C.r*C.r;
double delta = f*f - 4*e*g;
double t1,t2;
if(dcmp(delta) < 0) return 0; //相离
if(dcmp(delta) == 0) { //相切
t1 = t2 = -f / (2*e);
sol.push_back(p+v*t1);
return 1;
}
//相交
t1 = (-f - sqrt(delta)) / (2*e); sol.push_back(p+v*t1);
t2 = (-f + sqrt(delta)) / (2*e); sol.push_back(p+v*t2);
return 2;
}
//线段相交判定,不包含端点,相交返回 1,不交返回 0,当包含端点为<=
bool SPI(Point a1,Point a2,Point b1,Point b2){
double c1=Cross(a2-a1,b1-a1),c2=Cross(a2-a1,b2-a1),
c3=Cross(b2-b1,a1-b1),c4=Cross(b2-b1,a2-b1);
return dcmp(c1)*dcmp(c2)<=0 && dcmp(c3)*dcmp(c4)<=0;
}
//直线交点
Point GLI(Point p,Vector v,Point q,Vector w){
Vector u=p-q;
double t=Cross(w,u)/Cross(v,w);
return p+v*t;
}
bool check(Point &w,Vector &v){
int k=-1; Point nxt; double mn=1e9;
for(int i=0;i<n;i++) if (dcmp(Dot(a[i]-w,v))>0){
vector<Point> t;
int Xnum=getLineCircleIntersecion(w,v,Circle(a[i],r[i]),t);
if (Xnum!=2) continue;
if (Length(w-t[0])<Length(w-t[1])){
if (Length(w-t[0])<mn) mn=Length(w-t[0]),nxt=t[0],k=i;
}else
if (Length(w-t[1])<mn) mn=Length(w-t[1]),nxt=t[1],k=i;
}
if (k==-1){// 没有禁区,直达边界,看到达哪个边界:左,下,右
if (SPI(w,v*1e10,Point(x1,y2),Point(x1,y1)))
w=GLI(w,v,Point(x1,y2),Point(0,-1));
else
if (SPI(w,v*1e10,Point(x1,y1),Point(x2,y1)))
w=GLI(w,v,Point(x1,y1),Point(1,0));
else
w=GLI(w,v,Point(x2,y1),Point(0,1));
return false;
}
w=nxt;
if (dcmp(nxt.y-a[k].y)<=0) v=Vector(0,-1);else
if (nxt.x<a[k].x) v=Rotate(nxt-a[k],pi/2);
else v=Rotate(nxt-a[k],pi*1.5);
return true;
}
void get(Point w,Vector e,vector<Point> &q){
while(check(w,e)) q.pb(w);
q.pb(w); q.pb(Point(w.x,y1));
}
int main(int argc, char const *argv[])
{
cin>>n>>x1>>y1>>x2>>y2;
st.read();en.read();
for(int i=0;i<n;i++) a[i].read(),cin>>r[i];
vector<Point> A,B,C,D;
get(st,Vector(0,-1),A);
get(en,Vector(0,-1),B);
for(auto i:A) if (C.size()==0)C.pb(i);else
if (!(C.back()==i)) C.pb(i);
for(int i=(int)B.size()-1;i>=0;i--)
if (!(C.back()==B[i]))C.pb(B[i]);
int sz=C.si();
if (sz){//去重
for(int i=0,j;i<sz;i=j){
D.pb(C[i]);
j=i+1;
for(int k=sz-1;k>i;k--) if (C[k]==C[i]){
j=k+1; break;
}
}
}
cout<<D.size()<<'\n';
for(auto i:D)
printf("%.8f %.8f\n",i.x,i.y);
return 0;
}