测试地址:Freelancer’s Dreams
题目大意: 有
项工作,每项工作有两项属性
,表示做
单位时间可以获得
的经验和
的钱,现在要攒够
的经验和
的钱,且任意一个时刻只能做一项工作,每项工作进行的时间可以是任意非负实数,问要达到目标最少需要工作多少时间?
做法: 本题需要用到凸包。
好题。我们把一项工作看成一个向量,首先来看一个向量
可以满足的条件的区域,这显然是
这块区域,在二维平面中是一个边平行于坐标轴的矩形。
我们再看当总时间
时,两个向量
通过线性组合能构造出的向量的区域,我们知道
是两点间线段的一个方程,这就意味着这条线段是能构造出的向量区域的边界,因此两个向量加上这条向量的边界所构成的三角形就是所求的区域。
拓展到多个向量的情况,能构造出的区域就是原点加上这些向量点所构成的凸包了。能构造出的向量的区域是这个凸包,而根据上面的讨论,向量能满足的条件的区域是一个矩形,因此我们分别求出
坐标的最大值,加上
两个点做凸包,能得到的就是总时间
时能满足的条件的区域了。
那么现在要扩展到总时间
的情况,显然就是将这个凸包放大(或缩小)至原来的
倍。因此要求最小的
使得能满足条件
,实际上就是求一个
使得凸包正好包含点
。因为这个放大(或缩小)可以近似地看做关于原点位似,所以我们从原点向
连边,与总时间
的凸包交于点
,答案就是原点到
的距离与原点到
的距离的比值。那么我们就以
的时间复杂度解决了这一题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
const double eps=1e-7;
int n,st[100010],top;
double P,Q;
struct point
{
double x,y;
}p[100010];
double operator * (point a,point b) {return a.x*b.y-b.x*a.y;}
point operator + (point a,point b) {point s={a.x+b.x,a.y+b.y};return s;}
point operator - (point a,point b) {point s={a.x-b.x,a.y-b.y};return s;}
point operator * (point a,double b) {point s={a.x*b,a.y*b};return s;}
double dis(point a,point b)
{
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
bool cmp(point a,point b)
{
if (abs((a-p[1])*(b-p[1]))<eps)
return dis(p[1],a)<dis(p[1],b);
return (a-p[1])*(b-p[1])>0;
}
void graham_scan()
{
sort(p+2,p+n+1,cmp);
top=0;
for(int i=1;i<=n;i++)
{
while(top>1&&(p[st[top]]-p[st[top-1]])*(p[i]-p[st[top]])<eps)
top--;
st[++top]=i;
}
}
point inter(point p,point v,point q,point w)
{
point u=p-q;
double t=(w*u)/(v*w);
return p+(v*t);
}
int main()
{
scanf("%d%lf%lf",&n,&P,&Q);
double mxx=0.0,mxy=0.0;
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
mxx=max(mxx,p[i].x);
mxy=max(mxy,p[i].y);
}
p[++n].x=0.0,p[n].y=0.0;swap(p[1],p[n]);
p[++n].x=0.0,p[n].y=mxy;
p[++n].x=mxx,p[n].y=0.0;
graham_scan();
point Target={P,Q};
double ans;
for(int i=2;i<top;i++)
{
point joint=inter(p[1],Target,p[st[i]],p[st[i+1]]-p[st[i]]);
double x1=p[st[i]].x,y1=p[st[i]].y,x2=p[st[i+1]].x,y2=p[st[i+1]].y;
if (x1>x2) swap(x1,x2);
if (y1>y2) swap(y1,y2);
if (joint.x>=x1-eps&&joint.x<=x2+eps&&joint.y>=y1-eps&&joint.y<=y2+eps)
{
ans=sqrt(dis(p[1],Target)/dis(p[1],joint));
break;
}
}
printf("%.10lf",ans);
return 0;
}