[计蒜客16951] Out-out-control cars [2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 B]

题意

给定两个三角形的三点坐标和其速度矢量,询问两三角形是否会相撞,注意题目中描述,如果点相接触也算碰撞。

题解

首先求出相对速度,即让两速度矢量相减即可。这样就可以看做一个三角形静止,另一个运动了。此时,再求出两三角形垂直于速度方向的范围是多少,如图

如果两个范围不能相交一定不会相撞。
如果相交,则取相交范围任意一点,代码中取了相交部分的中点,那么过这一点的斜率为速度矢量斜率的直线会与两个三角形都相交,如果直线与一个三角形的交点在另一三角形内部,则相撞,否则各个三角形与直线相交任取一点,这两点的矢量与速度矢量相反即可相撞。另外,需要处理相对速度为零和初始时就相撞的情况。

代码

#include <bits/stdc++.h>

typedef double lf;

struct poi {
    lf x, y;
    poi operator - (const poi& B) const {poi a;a.x=x-B.x,a.y=y-B.y;return a;}
    lf operator * (const poi&B) const { return x*B.y-B.x*y; }
    void in() { scanf("%lf%lf",&x,&y); }
    void out() {
        printf("%.2lf %.2lf\n",x,y);
    }
}A,B,C,D,E,F,v1,v2;

typedef poi vec;
struct seg {
    poi a, b;
};

int dir(poi p,seg s) {
    lf tmp = (p-s.b)*(s.b-s.a);
    if (tmp>0)return 1;
    else if(tmp<0)return -1;
    return 0;
}

bool Inter() {
    int a,b,c;
    a=dir(A,(seg){D,E});b=dir(A,(seg){E,F});c=dir(A,(seg){F,D});
    if ((!a&&!b)||(!a&&!c)||(!b&&!c)||(!a&&b==c)||(!b&&a==c)||(!c&&a==b)||(a && a==b && b==c)) return true;
    a=dir(B,(seg){D,E});b=dir(B,(seg){E,F});c=dir(B,(seg){F,D});
    if ((!a&&!b)||(!a&&!c)||(!b&&!c)||(!a&&b==c)||(!b&&a==c)||(!c&&a==b)||(a && a==b && b==c)) return true;
    a=dir(C,(seg){D,E});b=dir(C,(seg){E,F});c=dir(C,(seg){F,D});
    if ((!a&&!b)||(!a&&!c)||(!b&&!c)||(!a&&b==c)||(!b&&a==c)||(!c&&a==b)||(a && a==b && b==c)) return true;
    a=dir(D,(seg){A,B});b=dir(D,(seg){B,C});c=dir(D,(seg){C,A});
    if ((!a&&!b)||(!a&&!c)||(!b&&!c)||(!a&&b==c)||(!b&&a==c)||(!c&&a==b)||(a && a==b && b==c)) return true;
    a=dir(E,(seg){A,B});b=dir(E,(seg){B,C});c=dir(E,(seg){C,A});
    if ((!a&&!b)||(!a&&!c)||(!b&&!c)||(!a&&b==c)||(!b&&a==c)||(!c&&a==b)||(a && a==b && b==c)) return true;
    a=dir(F,(seg){A,B});b=dir(F,(seg){B,C});c=dir(F,(seg){C,A});
    if ((!a&&!b)||(!a&&!c)||(!b&&!c)||(!a&&b==c)||(!b&&a==c)||(!c&&a==b)||(a && a==b && b==c)) return true;
    return false;
}

lf Dot(poi a,poi b) { return a.x*b.x+a.y*b.y; }

bool GetCross(lf k,lf b,poi A,poi B,poi&ret) {
    vec v = B-A;
    if (v.x==0) {
        lf y = k*A.x+b;
        if ((y>=A.y&&y<=B.y) || (y>=B.y&&y<=A.y)) {
            ret.x = A.x;ret.y = y;return true;
        }
        return false;
    }
    lf k2 = v.y/v.x;
    lf b2 = A.y-k2*A.x;
    if (k == k2) {
        if (b == b2) { ret = A; return true; }
        return false;
    }
    lf x = (b2-b)/(k-k2);
    lf y = k*x+b;
    if (x>=std::min(A.x,B.x)&&x<=std::max(A.x,B.x)&&y>=std::min(A.y,B.y)&&y<=std::max(A.y,B.y)) {
        ret.x = x;ret.y = y;return true;
    }
    return false;
}

poi GetPoint(lf k,lf b,poi A,poi B,poi C) {
    poi ans;
    if (GetCross(k,b,A,B,ans)) return ans;
    if (GetCross(k,b,B,C,ans)) return ans;
    if (GetCross(k,b,C,A,ans)) return ans;
    while(1);
}

inline void work() {
    A.in(),B.in(),C.in(),v1.in();
    D.in(),E.in(),F.in(),v2.in();
    v2 = v2-v1;
    if (Inter()) {puts("YES");return;}
    if (v2.x==0&&v2.y==0) {puts("NO");return;}
    if (v2.x==0) {
        std::swap(v2.x,v2.y);
        std::swap(A.x,A.y);
        std::swap(B.x,B.y);
        std::swap(C.x,C.y);
        std::swap(D.x,D.y);
        std::swap(E.x,E.y);
        std::swap(F.x,F.y);
    }
    lf k = v2.y/v2.x;
    lf b1,b2,b3,b1l,b1r,b2l,b2r;
    b1 = A.y-k*A.x;
    b2 = B.y-k*B.x;
    b3 = C.y-k*C.x;
    b1l=std::min(b1,std::min(b2,b3));
    b1r=std::max(b1,std::max(b2,b3));
    b1 = D.y-k*D.x;
    b2 = E.y-k*E.x;
    b3 = F.y-k*F.x;
    b2l=std::min(b1,std::min(b2,b3));
    b2r=std::max(b1,std::max(b2,b3));
    if (b1r<b2l || b1l>b2r) { puts("NO");return; }
    b1l=std::max(b1l,b2l);
    b1r=std::min(b1r,b2r);
    if(b1l>b1r){puts("NO");return;}
    b1l=(b1l+b1r)/2.0;
    poi p1=GetPoint(k,b1l,A,B,C);
    poi p2=GetPoint(k,b1l,D,E,F);
    p1=p2-p1;
    if (Dot(p1,v2)<0) puts("YES");
    else puts("NO");
}

int main() {
    int T;
    scanf("%d",&T);
    for(int i=1;i<=T;i++)printf("Case #%d: ",i),work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值