HDU 5027 Help!(三分+圆与线段的交点)

8 篇文章 0 订阅
4 篇文章 0 订阅

Help!

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 180    Accepted Submission(s): 32


Problem Description
“Help! Help!”

While walking in the park, you suddenly hear someone shouting for help. You immediately realize that a person has fallen into the lake. As a brave man, you decide to save him.

You are really familiar with the terrain of the park. The park can be regarded as a 2D plane. And the lake is a convex polygon. At current, you are on (Xo, Yo), and the person is on (Xp, Yp). You also know that you can run Vr per second on the land, or swim Vs per second in the lake. Notice that you are allowed to run along the edge of the lake.

You are not good at swimming. You cannot stay in the lake longer than Ts second. And carrying another person will cut down your swimming speed by half.

Can you save the poor guy? What is the minimum time for you to reach him, and carry him back to the border of the lake?
 

Input
There are several test cases in the input.

The first line contains an integer T (1 <= T <= 20) -- the number of test cases.

For each case:

The first line contains three real numbers Ts, Vr, Vs. 0 < Ts < 10 8, 0 < Vs < Vr < 10 8.

The second line contains two real numbers Xo, Yo, indicate the position (Xo, Yo) of you at current.

The third line contains two real numbers Xp, Yp, indicate the position (Xp, Yp) of the person you are going to save.

The forth line contains only one integer N -- the number of vertices of the lake. 3 <= N <= 50000.

The follow N lines, each line contains two real numbers x, y, indicating one of the vertex (x, y) of the lake. The vertices of lake are listed in either clockwise or counter-clockwise order.

Each coordinate in the input does not exceed 10 6 by its absolute value. Your position is on the land and the person’s is in the lake.
 

Output
For each test case, output the minimum time(in seconds) to save the poor person, rounded to two digits after the decimal point. If you cannot save he, output “-1” instead.
 

Sample Input
  
  
2 100 2 1 0 10 0 0 3 -1 1 1 1 0 -1 1 2 1 0 10 0 0 3 -1 1 1 1 0 -1
 

Sample Output
  
  
6.39 -1
 
题意:有个人掉进了水里,你要去救他,告诉你你的坐标和落水者的坐标,以及在岸上的速度和在水里的速度,(救到人游到岸边的速度是水里的一半)以及告诉你你在水里呆的最长时间,而且这个人最多进入水里一次。问你救完这个人的最小时间。
思路: 容易想到,当你救到人的时候,回到岸边的最小时间的是可以确定的。即为该点到n条线段的最短距离除以速度,记该时间为Tm。 先求当前点与凸包的切线,在两条切线间的点,人是可以直接过去的,直接三分答案(先求出两个端点,用直线与圆的交点求得, 进水点为(x,y)sqr(x-Xp)+sqr(y-Yp)=sqr((Ts-Tm)*Vs) ),注意要转化成线段需要分类讨论。
而另外的点,需要先到达切点,然后沿着边走到相应的边,因此先算好距离的前缀和(要算两个,从两个方向过去),再三分答案。
然后。。。C++ AC了。。。。G++ TLE。。。无力吐槽!!
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

const double eps=1e-4;
const double INF = 1e16;
const int maxn = 50000+10;
double sumL[maxn],sumR[maxn];
double Ts,Vr,Vs;
int dcmp(double x) {
    if(fabs(x) < eps) return 0;
    else if(x < 0) return -1;
    else return 1;
}
struct Point{
    double x,y;
    Point(double x0=0,double y0=0){
        x=x0,y=y0;
    }
    friend bool operator < (Point a,Point b){
        if(a.y!=b.y) return a.y<b.y;
        else return a.x<b.x;
    }
};

Point Poly[maxn],cur,targ;
int n;
typedef Point Vector;
double MinToLandT,ans;
inline 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 p) { return Vector(A.x*p,A.y*p); }
Vector operator / (Vector A,double p) { return Vector(A.x/p,A.y/p); }
bool operator == (const Point &a,const Point &b) { return dcmp(a.x-b.x)==0 && dcmp(a.y-b.y)==0 ; }
inline double sqr(double x) {
    return x*x;
}
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(Dot(A,B)/Length(A)/Length(B)); }
double Cross(Vector A,Vector B){ return A.x*B.y-A.y*B.x; }
Vector Rotate(Vector A,double rad){ return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad)); }
double torad(double ang){ return ang/180.0*acos(-1.0); }

void circle_cross_line(Point a,Point b,Point o,double r,Point ret[],int &num) {
    double x0 = o.x ,y0 = o.y;
    double x1 = a.x, y1 = a.y;
    double x2 = b.x, y2 = b.y;
    double dx = x2-x1, dy = y2-y1;
    double A = dx*dx+dy*dy;
    double B = 2*dx*(x1-x0) + 2*dy*(y1-y0);
    double C = sqr(x1-x0) + sqr(y1-y0)-sqr(r);
    double delta = B*B-4*A*C;
    num = 0;
    if( dcmp(delta) >= 0) {
        double t1 = (-B - sqrt(delta)) / (2*A);
        double t2 = (-B + sqrt(delta)) / (2*A);
        ret[num++] = Point(x1+t1*dx,y1+t1*dy);
        ret[num++] = Point(x1+t2*dx,y1+t2*dy);
    }
}
double DistanceToSegment(Point P,Point A,Point B){
    if(A==B) return Length(P-A);
    Vector v1=B-A,v2=P-A,v3=P-B;
    if(dcmp(Dot(v1,v2))<0) return Length(v2);
    else if(dcmp(Dot(v1,v3))>0) return Length(v3);
    else return fabs(Cross(v1,v2))/Length(v1);
}
double targetT(Point t,Point st) {
    if(dcmp(Length(t-targ)/Vs-Ts+MinToLandT) > 0) return INF;
    else return Length(t-st)/Vr+Length(t-targ)/Vs;

}
bool cmp(Vector a,Vector b) {
    if(dcmp(Cross(b , a)) > 0) return true;
    else if(dcmp(Cross(b,a))== 0) return abs(a.x) < abs(b.x);
    return false;
}
void input() {
    scanf("%lf%lf%lf",&Ts,&Vr,&Vs);
    scanf("%lf%lf",&cur.x,&cur.y);
    scanf("%lf%lf",&targ.x,&targ.y);
    scanf("%d",&n);
    for(int i = 0; i < n; i++) {
        scanf("%lf%lf",&Poly[i].x,&Poly[i].y);
    }
    Vector t1 = Poly[0]-Poly[1];
    Vector t2 = Poly[2]-Poly[1];
    if(dcmp(Cross(t1,t2)) > 0) {
        reverse(Poly,Poly+n);
    }
    MinToLandT = INF;
    for(int i = 0; i < n; i++) {
        MinToLandT = min(MinToLandT,DistanceToSegment(targ,Poly[i],Poly[(i+1)%n])*2/Vs);
    }
}
double thi_Search(Point p0 , Point p1, Point st){
    Point l = p0 , r = p1;
    while(Length(l-r) > eps){
        Point lmid = (l*2+r)/3 , rmid = (l+r*2)/3;
        if(targetT(lmid , st ) < targetT(rmid , st )) r = rmid;
        else l = lmid;
    }
    return targetT((l+r)/2 , st);
}

void init(int Lid,int Rid) {
    sumR[Rid] = Length(cur-Poly[Rid]);
    for(int i = (Rid+1)%n; i != Lid; i = (i+1)%n) {
        int before = (i-1+n)%n;
        sumR[i] = sumR[before] + Length(Poly[before]-Poly[i]);
    }
    sumL[Lid] = Length(cur-Poly[Lid]);
    for(int i = (Lid-1+n)%n; i != Rid; i = (i-1+n)%n) {
        int before = (i+1)%n;
        sumL[i] = sumL[before] + Length(Poly[before]-Poly[i]);
    }
}
bool PointOnSegment(Point o,Point a,Point b) {//o on segment (a,b)
    if(a.x > b.x) swap(a,b);
    return (dcmp(b.x-o.x) >= 0 && dcmp(o.x-a.x) >= 0);
}
void solve() {
    if(Ts < MinToLandT ) {
        puts("-1");
        return;
    }
    Vector vec[maxn];
    for(int i = 0; i < n; i++) {
        vec[i] = Poly[i]-cur;
    }
    sort(vec,vec+n,cmp);
    ans = INF;
    int Lid = 0,Rid = 0;
    Point L = vec[0]+cur,R;
    if(dcmp(Angle(vec[n-1],vec[n-2]))==0) R = vec[n-2]+cur;
    else R = vec[n-1]+cur;
    for(int i = 0; i < n; i++) {
        if(R==Poly[i]) Rid = i;
        if(L==Poly[i]) Lid = i;
    }
    init(Lid,Rid);
    for(int i = Lid; i != Rid; i = (i+1)%n) {
        Point o = targ;
        Point ret[2];
        int num;
        double r = (Ts-MinToLandT)*Vs;
        Point a = Poly[i],b = Poly[(i+1)%n];
        if(a.x > b.x) swap(a,b);
        circle_cross_line(a,b,o,r,ret,num);
        if(num==0) continue;
        if(num==1) {
            Point t = ret[0];
            if(PointOnSegment(t,a,b)) {
                ans = min(ans,Length(t-cur));
            }
        }else{
            Point ep1,ep2;
            if(PointOnSegment(a,ret[0],ret[1])) {
                ep1 = a;
            }else{
                ep1 = ret[0];
            }
            if(PointOnSegment(b,ret[0],ret[1])) {
                ep2 = b;
            }else{
                ep2 = ret[1];
            }
            ans = min(ans,thi_Search(ep1 , ep2 , cur));
        }
    }
    for(int i = Rid; i != Lid; i = (i+1)%n) {
        Point o = targ;
        Point ret[2];
        int num;
        double r = (Ts-MinToLandT)*Vs;
        Point a = Poly[i],b = Poly[(i+1)%n];
        if(a.x > b.x) swap(a,b);
        circle_cross_line(a,b,o,r,ret,num);
        double tmp1,tmp2;
        if(num==0) continue;
        if(num==1) {
            Point t = ret[0];
            if(PointOnSegment(t,a,b)) {
                tmp1 = sumL[(i+1)%n] + Length(t-b);
                tmp2 = sumR[i]+Length(t-a);
            }
        }else{
            Point ep1,ep2;
            if(PointOnSegment(a,ret[0],ret[1])) {
                ep1 = a;
            }else{
                ep1 = ret[0];
            }
            if(PointOnSegment(b,ret[0],ret[1])) {
                ep2 = b;
            }else{
                ep2 = ret[1];
            }
            tmp1 = sumL[(i+1)%n]/Vr+thi_Search(ep1 , ep2 , Poly[(i+1)%n]);
            tmp2 = sumR[i]/Vr+thi_Search(ep1 , ep2 , Poly[i]);
        }

        ans = min(tmp1,ans);
        ans = min(tmp2,ans);
    }
    ans += MinToLandT;
    if(ans >= INF) {
        puts("-1");
    }else{
        printf("%.2lf\n",ans);
    }
}
int main() {
    int ncase;
    cin >> ncase;
    while(ncase--) {
        input();
        solve();
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值