Codeforces 1252I Mission Possible

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值