测试地址:Freelancer’s Dreams
题目大意: 有
n
n
n项工作,每项工作有两项属性
a
i
,
b
i
a_i,b_i
ai,bi,表示做
1
1
1单位时间可以获得
a
i
a_i
ai的经验和
b
i
b_i
bi的钱,现在要攒够
p
p
p的经验和
q
q
q的钱,且任意一个时刻只能做一项工作,每项工作进行的时间可以是任意非负实数,问要达到目标最少需要工作多少时间?
做法: 本题需要用到凸包。
好题。我们把一项工作看成一个向量,首先来看一个向量
(
a
,
b
)
(a,b)
(a,b)可以满足的条件的区域,这显然是
{
(
p
,
q
)
∣
p
≤
a
,
q
≤
b
}
\{(p,q)|p\le a,q\le b\}
{(p,q)∣p≤a,q≤b}这块区域,在二维平面中是一个边平行于坐标轴的矩形。
我们再看当总时间
≤
1
\le 1
≤1时,两个向量
(
a
1
,
b
1
)
,
(
a
2
,
b
2
)
(a_1,b_1),(a_2,b_2)
(a1,b1),(a2,b2)通过线性组合能构造出的向量的区域,我们知道
θ
⋅
(
a
1
,
b
1
)
+
(
1
−
θ
)
⋅
(
a
2
,
b
2
)
(
0
≤
θ
≤
1
)
\theta\cdot (a_1,b_1)+(1-\theta)\cdot (a_2,b_2)(0\le \theta \le 1)
θ⋅(a1,b1)+(1−θ)⋅(a2,b2)(0≤θ≤1)是两点间线段的一个方程,这就意味着这条线段是能构造出的向量区域的边界,因此两个向量加上这条向量的边界所构成的三角形就是所求的区域。
拓展到多个向量的情况,能构造出的区域就是原点加上这些向量点所构成的凸包了。能构造出的向量的区域是这个凸包,而根据上面的讨论,向量能满足的条件的区域是一个矩形,因此我们分别求出
x
,
y
x,y
x,y坐标的最大值,加上
(
0
,
m
a
x
y
)
,
(
m
a
x
x
,
0
)
(0,maxy),(maxx,0)
(0,maxy),(maxx,0)两个点做凸包,能得到的就是总时间
≤
1
\le 1
≤1时能满足的条件的区域了。
那么现在要扩展到总时间
≤
t
\le t
≤t的情况,显然就是将这个凸包放大(或缩小)至原来的
t
t
t倍。因此要求最小的
t
t
t使得能满足条件
(
p
,
q
)
(p,q)
(p,q),实际上就是求一个
t
t
t使得凸包正好包含点
(
p
,
q
)
(p,q)
(p,q)。因为这个放大(或缩小)可以近似地看做关于原点位似,所以我们从原点向
(
p
,
q
)
(p,q)
(p,q)连边,与总时间
≤
1
\le 1
≤1的凸包交于点
P
P
P,答案就是原点到
(
p
,
q
)
(p,q)
(p,q)的距离与原点到
P
P
P的距离的比值。那么我们就以
O
(
n
log
n
)
O(n\log n)
O(nlogn)的时间复杂度解决了这一题。
以下是本人代码:
#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;
}