HDU - 4128 Running relay(贪心+凸包+三分)

点我看题

题意:n个人参加一个接力赛,跑道长度为L,每个人根据跑步时的心情的不同速度也不一样,bad mood时速度为s,good mood时速度为t,每个人至少跑d米,当满足所有人都在badmood下且花费的时间不超过w的情况下,如果所有人都以goodmood跑,至少要花费多少时间。

分析:参考的HDU 4128 Running relay (贪心+凸包优化)这篇博客

参考代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>

using namespace std;
#define eps 1e-6
const int maxn = 1e4+10;
int n;
double d,len,w;

int sgn( double x)
{
    if( x > eps)//x>0
        return 1;
    else if( x < -eps)//x==0
        return -1;
    else//x<0
        return 0;
}

//x:s,y:t
//用点的横纵坐标来记录runner的速度值
struct Point{
    double x,y;
    Point(){}
    Point( double xx, double yy)
    {
        x = xx;
        y = yy;
    }
    Point operator - ( const Point &p)const
    {
        return Point(x-p.x,y-p.y);
    }
    double operator ^ ( const Point &p)const
    {
        return x*p.y-p.x*y;
    }
    bool operator < ( const Point &p)const
    {
        return sgn(x-p.x) < 0 || (sgn(x-p.x) == 0 && sgn(y-p.y) < 0);
    }
}p[maxn],tmp[maxn];

double xmult( Point p1, Point p2, Point p3)
{
    return (p2-p1)^(p3-p1);
}

//找凸包,返回节点数
int SetConvex( Point p[], int n)
{
    int top = 0;
    if( n == 0)
        return 0;
    tmp[top++] = p[0];
    if( n == 1)
        return 1;
    tmp[top++] = p[1];
    for( int i = 2; i < n; i++)
    {
        while( top >= 2 && sgn(xmult(tmp[top-2],tmp[top-1],p[i])) <= 0)
            top--;
        tmp[top++] = p[i];
    }
    return top;
}

//三分
double ternary( Point a, int l, int r)
{
    double k = 1e30;
    while( l <= r)
    {
        int lm = (2*l+r)/3;//l+(r-l)*1/3
        int rm = (l+2*r)/3;//l+(r-l)*2/3
        double ll = (a.y-tmp[lm].y)/(a.x-tmp[lm].x);
        double rr = (a.y-tmp[rm].y)/(a.x-tmp[rm].x);
        if( sgn(ll-rr) >= 0)
        {
            k = min(k,rr);
            l = lm+1;
        }
        else
        {
            k = min(k,ll);
            r = rm-1;
        }
    }
    return a.y*len+(w-a.x*len)*k;
}

double work()
{
    if( sgn(len) < 0 || sgn(w) < 0)//不满足条件
        return -1;
    sort(p,p+n);
    if( sgn(w-p[0].x*len) < 0)//badmood下跑的最快的runner都跑不及
        return -1;
    int j = 1;
    for( int i = 0; i < n; i++)
    {
        if( sgn(p[i].y-p[j-1].y) >= 0)//goodmood下后面的速度小,不保存
            continue;
        p[j++] = p[i];//速度大,保存下来
    }
    n = j;
    if( n == 1)
        return p[0].y*len;
    for( j = 0; j < n && sgn(w-p[j].x*len) >= 0; j++)//找出临界点
        ;
    int tot = j;//记录单个满足条件的数量
    int sz = SetConvex(p+j,n-j);
    double ans = 1e30;
    for( int i = 0; i < tot; i++)
    {
        ans = min(ans,p[i].y*len);
        ans = min(ans,ternary(p[i],0,sz-1));
    }
    return ans;
}

int main()
{
    int T;
    scanf("%d",&T);
    while( T--)
    {
        scanf("%d%lf%lf%lf",&n,&d,&len,&w);
        double s,t;
        double base = 0;//基础时间(也就是每个人都要跑的)
        for( int i = 0; i < n; i++)
        {
            scanf("%lf%lf",&s,&t);
            p[i].x = s;
            p[i].y = t;
            len -= d;
            w -= p[i].x*d;
            base += p[i].y*d;
        }
        double add = work();
        if( sgn(add) < 0)
            puts("No solution");
        else
            printf("%.2f\n",base+add);
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值