HDU 4128 Running relay (贪心+凸包优化)

题解

一个很容易YY的结论:最多只有两个人跑,难在证明。题解中的x+y=1,x*sa+y*sc=sb这两个等式太犀利了。
然后就是解方程组:
li + lj = L
li*si + lj*sj = W
ans = li*Ti + lj*Tj
li表示i跑的距离。
解出ans的表达式后可以转化成点斜式的方程,求出凸包后3分求解。
/* Created Time: Sunday, November 10, 2013 PM11:34:14 CST */
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 10007;
const double eps = 1e-6;
int n,d;
double L,W;
inline int sig(double x) { return (x>eps)-(x<-eps); }
// Si:x , Ti:y
struct point {
    double x,y;
    point(int _x=0,int _y=0) : x(_x),y(_y) {}
    bool operator < (const point &tt) const {
        return sig(x-tt.x)<0 || sig(x-tt.x)==0 && sig(y-tt.y)<0;
    }
    point operator - (const point &tt) const { return point(x-tt.x,y-tt.y); }
}dot[N],con[N];
inline double cross(point a,point b) { return a.x*b.y-a.y*b.x; }
int set_convex(point *dot,int n) {
    int top = 0;
    if (n==0) return 0;
    con[top++] = dot[0];
    if (n==1) return 1;
    con[top++] = dot[1];
    for (int i = 2; i < n; i ++) {
        while (top>=2 && sig(cross(con[top-1]-con[top-2],dot[i]-con[top-2]))<=0) top --;
        con[top++] = dot[i];
    }
    return top;
}
double ternary(point a,int l,int r) {
    double slop = 1e30;
    while (l<=r) {
        int lfd = (2*l+r)/3;
        int rfd = (l+2*r)/3;
        double ll = (a.y-con[lfd].y)/(a.x-con[lfd].x);
        double rr = (a.y-con[rfd].y)/(a.x-con[rfd].x);
        if (sig(ll-rr)>=0) {
            l = lfd+1;
            slop = min(slop,rr);
        } else {
            r = rfd-1;
            slop = min(slop,ll);
        }
    }
    return a.y*L+(W-a.x*L)*slop;
}
double work() {
    double ret = 1e30;
    if (sig(W)<0 || sig(L)<0) return -1;
    sort(dot,dot+n);
    int j = 1;
    for (int i = 0; i < n; i ++) {
        if (sig(dot[i].y-dot[j-1].y)>=0) continue;
        dot[j++] = dot[i];
    }
    n = j;
    if (sig(W-L*dot[0].x)<0) return -1;
    if (n==1) return L*dot[0].y;
    for (j = 0; j<n && sig(W-L*dot[j].x)>=0; j ++);
    int tot = j;
    int sz = set_convex(dot+j,n-j);
    for (int i = 0; i < tot; i ++) {
        ret = min(ret,L*dot[i].y);
        ret = min(ret,ternary(dot[i],0,sz-1));
    }
    return ret;
}
int main() {
    int cas;
    scanf("%d",&cas);
    while (cas--) {
        scanf("%d%d%lf%lf",&n,&d,&L,&W);
        double base = 0;
        for (int i = 0; i < n; i ++) {
            int a,b;
            scanf("%d%d",&a,&b);
            dot[i].x = a;
            dot[i].y = b;
            W -= dot[i].x*d;
            L -= d;
            base += dot[i].y*d;
        }
        double ans = work();
        if (sig(ans)<0) puts("No solution");
        else printf("%.2f\n",ans+base);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值