POJ 1755 Triathlon(半平面交)

题目链接:http://poj.org/problem?id=1755

题意:一段距离总长度为L,将L分成三部分a,b和c(a、b、c均大于0)。有N(1 <= N <= 100) 个人,第i个人在这三段中的速度分别是Vi,Ui和Wi(1 <= Vi, Ui, Wi <= 10000) 。问是否存在一种分法,使得第i个人可以成为冠军(并列冠军不算,也就是只有i一个人是冠军)。

思路:对于某种分法,第i个人用的时间ti=a/Vi+b/Ui+c/Wi,第j个人用的时间tj=a/Vj+b/Uj+c/Wj,那么本题的要求就是对于i(1<=i<=N),是否有可能对于所有的j(1<=j<=N 且j!=i),满足ti-tj<0?即(1/Vi-1/Vj)*a+(1/Ui-1/Uj)*b+(1/Wi-1/Wj)*c<0 。两边同时除以c得(令X=a/c,Y=b/c,A=1/Vi-1/Vj,B=1/Ui-1/Uj,C=1/Wi-1/Wj):A*X+B*Y+C<0。到此,可以看出,就是一个半平面求交的问题。然后每个人用其他人交一下,求出最后的面积,大于0即可。我们注意到X=a/c>=0,Y=b/c>=0(其实严格按照题意来说是大于0),X+Y=(a+b)/c=(L-c)/c=L/c-1,因此初始化时必然是一顶点在原点且在第一象限的等腰三角形。至于这个等腰三角形的两个腰长是多少为保险起见建议定的大一点,我是定的100000。

 



int DB(double x)
{
    if(x>1e-25) return 1;
    if(x<-1e-25) return -1;
    return 0;
}

struct point
{
    double x,y;

    point(){}
    point(double _x,double _y)
    {
        x=_x;
        y=_y;
    }

    void read()
    {
        RD(x,y);
    }

    void output()
    {
        printf("(%.2lf %.2lf)",x,y);
    }

    point operator+(point a)
    {
        return point(x+a.x,y+a.y);
    }
    point operator-(point a)
    {
        return point(x-a.x,y-a.y);
    }

    double operator*(point a)
    {
        return x*a.y-y*a.x;
    }

    point operator*(double t)
    {
        return point(x*t,y*t);
    }

    point operator/(double t)
    {
        return point(x/t,y/t);
    }

    bool operator==(point a)
    {
        return DB(x-a.x)==0&&DB(y-a.y)==0;
    }

    bool operator!=(point a)
    {
        return DB(x-a.x)||DB(y-a.y);
    }
};

struct line
{
    double a,b,c;

    line(){}
    line(double _a,double _b,double _c)
    {
        a=_a;
        b=_b;
        c=_c;
    }

    line(point p,point q)
    {
        a=q.y-p.y;
        b=p.x-q.x;
        c=p.y*q.x-p.x*q.y;
    }
};

point a[N];
int n;


//t在ab的左侧返回正值
double cross(point a,point b,point t)
{
    return point(b-a)*point(t-a);
}


point getCross(point a,point b,point p,point q)
{
    double s1=(a-p)*(b-p);
    double s2=(b-q)*(a-q);
    double t=s1+s2;
    return (p*s2+q*s1)/(s1+s2);
}


point getCross(line L1,line L2)
{
    point p;
    p.x=(L2.c*L1.b-L1.c*L2.b)/(L1.a*L2.b-L2.a*L1.b);
    if(DB(L2.b)) p.y=-(L2.a*p.x+L2.c)/L2.b;
    else p.y=-(L1.a*p.x+L1.c)/L1.b;
    return p;
}


double getDist(point a,point b)
{
    return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}

double getArea(point p[],int n)
{
    double ans=0;
    int i;
    p[n]=p[0];
    FOR0(i,n) ans+=p[i]*p[i+1];
    return ans/2;
}

void moveSegment(point &p1,point &p2,double r)
{
    r/=getDist(p1,p2);
    point p=point(p1.y-p2.y,p2.x-p1.x)*r;
    p1=p1+p;
    p2=p2+p;
}


int m,V[N],U[N],W[N];


double cal(double A,double B,double C,point a)
{
    return A*a.x+B*a.y+C;
}



void cut(point a[],int &n,double A,double B,double C)
{
    if(!n) return;
    point b[N],p;
    int i,m=n,t1,t2;
    FOR0(i,n) b[i]=a[i];
    b[n]=b[0];
    n=0;
    FOR0(i,m)
    {
        t1=DB(cal(A,B,C,b[i]));
        t2=DB(cal(A,B,C,b[i+1]));
        p=getCross(line(A,B,C),line(b[i],b[i+1]));
        if(!t1&&t2<0||t1<0&&t2<0||t1<0&&!t2||!t1&&!t2)
        {
            a[n++]=b[i];
            a[n++]=b[i+1];
        }
        else if(t1>0&&!t2) a[n++]=b[i+1];
        else if(t1>0&&t2<0) a[n++]=p,a[n++]=b[i+1];
        else if(t1<0&&t2>0) a[n++]=b[i],a[n++]=p;
        else if(!t1&&t2>0) a[n++]=b[i];
    }
    m=1;
    FOR1(i,n-1) if(a[i]!=a[i-1]) a[m++]=a[i];
    if(a[m]==a[0]) m--;
    n=m;
}

int OK(int i,int j)
{
    return V[i]==V[j]&&U[i]==U[j]&&W[i]==W[j];
}

int main()
{
    RD(m);
    int i,j,flag;
    double A,B,C;
    FOR1(i,m) RD(V[i],U[i],W[i]);
    FOR1(i,m)
    {
        a[0]=point(0,0);
        a[1]=point(100000,0);
        a[2]=point(0,100000);
        n=3;
        flag=0;
        FOR1(j,m) if(i!=j)
        {
            if(flag||OK(i,j))
            {
                flag=1;
                continue;
            }
            A=1.0/V[i]-1.0/V[j];
            B=1.0/U[i]-1.0/U[j];
            C=1.0/W[i]-1.0/W[j];
            cut(a,n,A,B,C);
        }
        if(!flag&&DB(fabs(getArea(a,n)))) puts("Yes");
        else puts("No");
    }
    return 0;
}

  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值