Codeforces Round #335 (Div. 1) C. Freelancer's Dreams

题意

有n 种工作,做1 单位时间的第i 种工作可以获得ai经验和bi 金钱,现在需要p 经验和q 金钱,问至少需要工作多久。工作时间可以不是整数

题解

一个显然的结论
如果你把一个点看做一个点
那么两个点 (x1,y1) ( x 1 , y 1 ) (x2,y2) ( x 2 , y 2 )
那么他可以取到这两个点间的所有值
先新加入三个点 (0,0) ( 0 , 0 ) , (xmax,0) ( x m a x , 0 ) , (0,ymax) ( 0 , y m a x )
那么我们显然的可以先求出一个凸包
那么我们考虑一个点 (p,q) ( p , q )
他的最优方案
肯定是 (0,0) ( 0 , 0 ) (p,q) ( p , q ) 这条射线和凸包的交
我们一直用这个点肯定是最优的
那么就除一下就出来了

然后我发现我以前凸包的板子有点问题,就是我没有把点去重,这样的话会导致凸包求出来不是对的。这个的话应该很容易举出反例吧。。对拍的时候发现了这个问题啊
CODE:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const double eps=1e-7;
const int N=100005;
int n;
double p,q;
struct qq
{
    double x,y;
    qq (){}
    qq (double _x,double _y){x=_x;y=_y;}
}s[N];
int tot;
double mx=0,my=0;
double mul(qq x,qq y,qq z)
{
    double x1=x.x-z.x,y1=x.y-z.y;
    double x2=y.x-z.x,y2=y.y-z.y;
    return x1*y2-x2*y1;
}
double dis (qq x,qq y){return (x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y);}
bool cmp (qq x,qq y)
{
    if (abs(mul(x,y,s[1]))<eps) return dis(x,s[1])<dis(y,s[1]);
    return mul(x,y,s[1])>eps;
}
int sta[N],top;
void get ()//求一个凸包 
{
    sta[1]=1;sta[2]=2;top=2;
    for (int u=3;u<=tot;u++)
    {
        while (top>2&&mul(s[u],s[sta[top]],s[sta[top-1]])>eps) top--;
        sta[++top]=u;
    }
    tot=top;
    for (int u=1;u<=top;u++)    s[u]=s[sta[u]];
}
bool check (int x)//0,0和这个的连线是否在他的下面 
{
    return s[x].y*p<=s[x].x*q;
}
qq get (qq a,qq b,qq c,qq d)//求出这两条直线的交点 
{
     A=mul(b,c,d),B=mul(c,a,d);
    double xx,yy;
    xx=b.x+(double)(a.x-b.x)*(double)A/(A+B);
    yy=b.y+(double)(a.y-b.y)*(double)A/(A+B);
    return qq(xx,yy);
}
int main()
{
    scanf("%d%lf%lf",&n,&p,&q);
    tot=1;s[tot]=qq(0,0);
    for (int u=1;u<=n;u++)  
    {
        tot++;
        scanf("%lf%lf",&s[tot].x,&s[tot].y);
        mx=max(mx,s[tot].x);my=max(my,s[tot].y);
    }
    tot++;s[tot]=qq(mx,0);
    tot++;s[tot]=qq(0,my);
    sort(s+2,s+1+tot,cmp);
    int ToT=tot;    tot=1;
for (int u=2;u<=ToT;u++)
        if (s[tot].x!=s[u].x||s[tot].y!=s[u].y)
            s[++tot]=s[u];  
        /*  for (int u=1;u<=tot;u++)
        printf("%lf %lf\n",s[u].x,s[u].y);*/
    get();

    int l=1,r=tot-1;
    int lalal;//求出第一个点的连线是小于等于这个的 
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (check(mid)) {lalal=mid;l=mid+1;}
        else r=mid-1;
    }
    if (s[lalal].y*p==s[lalal].x*q)//如果是在一条直线上
    {
        if (s[lalal].x==0) printf("%.8lf\n",(double)q/s[lalal].y);
        else printf("%.8lf\n",(double)p/s[lalal].x);
    }
    else
    {
        qq x=get(s[lalal],s[lalal+1],qq(p,q),qq(0,0));
    //  printf("%lf %lf\n",s[lalal].x,s[lalal].y);
    //  printf("%lf %lf\n",s[lalal+1].x,s[lalal+1].y);
        if (x.x>eps)    printf("%.8lf\n",p/x.x);
        else printf("%.8lf\n",q/x.y);
    } 
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值