【CF605C】Freelancer's Dreams-凸包

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Maxwei_wzj/article/details/83242944

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

#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;
}

猜你喜欢

转载自blog.csdn.net/Maxwei_wzj/article/details/83242944
今日推荐