20200603 hz T3 安排【分数规划,差分→线性约束→半平面交】

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题目分析:

把题目要求的转化一下,首先二分答案 Z = min ⁡ x > 1 d x d 1 Z=\frac {\min_{x>1} d_x}{d_1} Z=d1minx>1dx,那么就是判断是否存在一组 x i x_i xi 满足 ( min ⁡ i = 1 m − 1 ∑ j = 1 n ( A i , j − Z ∗ B j ) ∗ x j ) ≥ 0 , ∀ x i ≥ 0 , ∃ x i > 0 \left(\min_{i=1}^{m-1} \sum_{j=1}^n(A_{i,j}-Z*B_j)*x_j\right)\ge 0,\forall x_i\ge 0,\exist x_i>0 (i=1minm1j=1n(Ai,jZBj)xj)0xi0xi>0
因为原题目要求 c c c不升,那么令 x j = c j − c j + 1 x_j=c_j-c_{j+1} xj=cjcj+1,就去掉了约束条件,与此同时 A i , j A_{i,j} Ai,j以及 B j B_j Bj的意义就改为自己那组的 a a a的前缀和。

m = 2 m=2 m=2,就是看是否有 j j j满足 A i , j − z ∗ B j ≥ 0 A_{i,j}-z*B_j\ge0 Ai,jzBj0,如果有就满足条件。

m = 3 m=3 m=3,对每个 j j j,把 A 1 , j − Z B j A_{1,j}-ZB_j A1,jZBj看做横坐标, A 2 , j − Z B j A_{2,j}-ZB_j A2,jZBj看做纵坐标, x j x_j xj相当于可以在这条射线上动,那么二者求和后取 m i n min min就是横坐标和纵坐标的较小值。
于是问题等价于在 n n n条射线上选一个点(向量),求和后使其落在第一象限。
如果初始时没有射线在第一象限,那么等价于判断是否所有射线(点)都在一个过原点的半平面内。
可以设一个过原点的方向向量 ( − 1 , k ) (-1,k) (1,k)代表一个半平面,其中 k > 0 k>0 k>0。一个点 ( x , y ) (x,y) (x,y)要在此半平面左侧,则 ( − 1 , k ) × ( x , y ) ≥ 0 (-1,k)\times(x,y)\ge 0 (1,k)×(x,y)0。对于 n n n个点都要满足限制,如果 k k k无解则说明二分的值可以达到。

m = 4 m=4 m=4,设这个半超平面的法向量为 ( 1 , a , b ) (1,a,b) (1,a,b),其中 a > 0 , b > 0 a>0,b>0 a>0,b>0. 一个点在此半超平面左侧,则与法向量的点积 ≤ 0 \le 0 0。即 ( 1 , a , b ) ⋅ ( x , y , z ) ≤ 0 (1,a,b)\cdot (x,y,z)\le 0 (1,a,b)(x,y,z)0
m = 2 m=2 m=2也可以用这个方法,其实更通用的方法是设 a x + b y + z ax+by+z ax+by+z为一个半超平面,带入点就是 a u + b v + w ≤ 0 au+bv+w\le 0 au+bv+w0

此时的限制就是 n n n x + a y + b z ≤ 0 x+ay+bz\le 0 x+ay+bz0,这相当于是一个二维平面上的半平面交( a a a看做横坐标, b b b看做纵坐标),如果交出来无解则说明二分的值可以达到。(注意不要漏掉 a > 0 , b > 0 a>0,b>0 a>0,b>0
限制写成向量的形式可以分类讨论一下,先令 a , b a,b a,b分别等于0求出线上两点,如果 y > 0 y>0 y>0则说明应在直线左侧,否则应在直线右侧,然后根据点的符号确定方向。
普通的半平面交实际上是判不了无解的,所以我们需要在外层框一个大矩形,如果最后留下的直线小于等于两条则说明无解。

Code(eps开成1e-6会WA):

#include<bits/stdc++.h>
#define maxn 40015
using namespace std;
const double inf = 1e16, eps = 1e-8;
int n,k,a[maxn],b[maxn];
namespace task1{
	void main(){
		double ans=0,s[2]={0};
		for(int i=1;i<=n;i++){
			s[b[i]!=0]+=a[i];
			ans=max(ans,s[1]/s[0]);
		}
		printf("%.6f\n",ans);
	}
}
int A[4][maxn];
double sgn(double x){return x>eps?1:x<-eps?-1:0;}
namespace task2{
	bool check(double Z){
		double L=eps,R=inf;
		for(int i=1;i<=n&&L<=R;i++){
			double x=A[1][i]-Z*A[0][i], y=A[2][i]-Z*A[0][i];
			if(!sgn(x)) {if(-y<0) return 1;}
			else if(x>0) R=min(R,-y/x);
			else L=max(L,-y/x);
		}
		return L>R;
	}
}
namespace task3{
	struct Pt{
		double x,y;
		Pt(double x=0,double y=0):x(x),y(y){}
		Pt operator + (const Pt &p)const{return Pt(x+p.x,y+p.y);}
		Pt operator - (const Pt &p)const{return Pt(x-p.x,y-p.y);}
		double operator * (const Pt &p)const{return x*p.y-y*p.x;}
		Pt operator * (const double &t)const{return Pt(x*t,y*t);}
	}p[maxn];
	struct Line{
		Pt p,v; double ang;
		Line(Pt p=0,Pt v=0):p(p),v(v),ang(atan2(v.y,v.x)){}
		bool operator < (const Line &L)const{return sgn(ang-L.ang)? ang<L.ang:L.v*(p-L.p)>=0;}
	}L[maxn],q[maxn];
	bool Onleft(Pt p,Line L){return L.v*(p-L.p)>0;}
	int cnt,l,r;
	Pt Inter(Line a,Line b){return a.p+a.v*((b.v*(a.p-b.p))/(a.v*b.v));}
	bool HalfPlane(){
		sort(L+1,L+1+cnt);
		q[l=r=1]=L[1];
		for(int i=2;i<=cnt;i++) if(sgn(L[i].ang-L[i-1].ang)){
			while(l<r&&!Onleft(p[r-1],L[i])) r--;
			while(l<r&&!Onleft(p[l],L[i])) l++;
			q[++r]=L[i],p[r-1]=Inter(q[r-1],q[r]);
		}
		while(l<r-1&&!Onleft(p[r-1],q[l])) r--;
		return r-l+1<=2;
	}
	bool check(double Z){
		L[cnt=1]=Line(Pt(0,0),Pt(0,-1)),L[cnt=2]=Line(Pt(0,0),Pt(1,0));
		L[++cnt]=Line(Pt(inf,0),Pt(0,1)),L[++cnt]=Line(Pt(inf,inf),Pt(-1,0));
		for(int i=1;i<=n;i++){
			double x=A[1][i]-Z*A[0][i],y=A[2][i]-Z*A[0][i],z=A[3][i]-Z*A[0][i];
			if(!sgn(y)&&!sgn(z)) {if(x>0) return 1;}
			else{
				if(!sgn(y)){
					if(sgn(z)>0) L[++cnt]=Line(Pt(0,-x/z),Pt(-1,0));
					else L[++cnt]=Line(Pt(0,-x/z),Pt(1,0));
				}
				else if(!sgn(z)){
					if(sgn(y)>0) L[++cnt]=Line(Pt(0,-x/y),Pt(-1,0));
					else L[++cnt]=Line(Pt(0,-x/y),Pt(1,0));
				}
				else{
					if(sgn(y)==sgn(-x/z)) L[++cnt]=Line(Pt(-x/y,0),Pt(x/y,-x/z));
					else L[++cnt]=Line(Pt(0,-x/z),Pt(-x/y,x/z));
				}
			}
		}
		return HalfPlane();
	}
}
int main()
{
	freopen("arrangement.in","r",stdin);
	freopen("arrangement.out","w",stdout);
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) scanf("%d",&b[i]),b[i]--;
	if(k==2) {task1::main();return 0;}
	for(int i=1;i<=n;i++){
		for(int j=0;j<=3;j++) A[j][i]=A[j][i-1];
		A[b[i]][i]+=a[i];
	}
	double l=0,r=1e10,mid;
	while(r-l>1e-6) mid=(l+r)*0.5,(k==3?task2::check(mid):task3::check(mid))?(l=mid):(r=mid);
	printf("%.6f\n",l);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值