[好题][半平面交][线性规划]Triathlon POJ1755

Triathlon is an athletic contest consisting of three consecutive sections that should be completed as fast as possible as a whole. The first section is swimming, the second section is riding bicycle and the third one is running.

The speed of each contestant in all three sections is known. The judge can choose the length of each section arbitrarily provided that no section has zero length. As a result sometimes she could choose their lengths in such a way that some particular contestant would win the competition.

Input

The first line of the input file contains integer number N (1 <= N <= 100), denoting the number of contestants. Then N lines follow, each line contains three integers Vi, Ui and Wi (1 <= Vi, Ui, Wi <= 10000), separated by spaces, denoting the speed of ith contestant in each section.

Output

For every contestant write to the output file one line, that contains word "Yes" if the judge could choose the lengths of the sections in such a way that this particular contestant would win (i.e. she is the only one who would come first), or word "No" if this is impossible.

Sample Input

9
10 2 6
10 7 3
5 6 7
3 2 7
6 2 6
3 5 7
8 4 6
10 4 2
1 8 7

Sample Output

Yes
Yes
Yes
No
No
No
Yes
No
Yes

题意: 现在有n位运动员参加铁人三项,给出每人在这三项中的速度,你可以随意分配这三项的路程,但是长度不能为零,对于每位运动员询问是否可以通过改变这三段路程使他成为第一,这里的第一指通过这三段路程的时间和严格小于其他运动员。

分析: 对于每位运动员单独处理,设三段路程长度为x,y,z使得他能够获得第一,那么x,y,z一定满足x/u[i] + y/v[i] + z/w[i] > x/u + y/v + z/w这n-1条不等式,其中u[i]、v[i]、w[i]是第i位其他运动员的三段速度,u、v、w是当前运动员的三段速度。显然这是一个三元一次方程组,不好解决,如果对于每条不等式两边都除z,那么就可以把x/z和y/z看作两个变量,由于我们只需要判断是否有解,并不需要解出具体的值,这样处理是没问题的。这样就得到了n-1个二元一次方程,用线性规划的方法画线切割矩形看最终是否存在可行域,如果不存在可行域说明无解,否则就是有解。

大体思路就是上面这样,但是这道题对精度要求非常高,eps必须设置为1e-16,另外半平面交的面积如果小于eps的话算作一个单点,这时候虽然存在可行域但是同样无解。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
//#define double long double
using namespace std;
const double eps = 1e-16;
const double inf = 1e15;
const double pi = acos(-1.0);
const int maxp = 1000;
int sgn(double x)
{
	if(fabs(x) < eps)return 0;
	if(x < 0)return -1;
	else return 1;
}
struct Point
{
	double x, y;
	Point(){}
	Point(double _x,double _y){x = _x, y = _y;}
	void input(){scanf("%lf%lf",&x,&y);}
	void output(){printf("%.2f %.2f\n",x,y);}
	bool operator == (Point b)const{return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;}
	bool operator < (Point b)const{return sgn(x-b.x)== 0?sgn(y-b.y)<0:x<b.x;}
	Point operator -(const Point &b)const{return Point(x-b.x,y-b.y);}
	//叉积
	double operator ^(const Point &b)const{return x*b.y - y*b.x;}
	//点积
	double operator *(const Point &b)const{return x*b.x + y*b.y;}
	//返回长度
	double len(){return hypot(x,y);/*库函数*/}
	//返回长度的平方
	double len2(){return x*x + y*y;}
	//返回两点的距离
	double distance(Point p){return hypot(x-p.x,y-p.y);}
	Point operator +(const Point &b)const{return Point(x+b.x,y+b.y);}
	Point operator *(const double &k)const{return Point(x*k,y*k);}
	Point operator /(const double &k)const{return Point(x/k,y/k);}
}; 
struct Line
{
	Point s,e;
	Line(){}
	Line(Point _s,Point _e){s = _s, e = _e;}
	bool operator ==(Line v){return (s == v.s)&&(e == v.e);}
	//`根据一个点和倾斜角angle确定直线,0<=angle<pi`
	Line(Point p,double angle)
	{
		s = p;
		if(sgn(angle-pi/2) == 0){e = (s + Point(0,1));}
		else{e = (s + Point(1,tan(angle)));}
	}
	void input()
	{
		s.input();
		e.input();
	}
	bool parallel(Line v){return sgn((e-s)^(v.e-v.s)) == 0;/*两向量叉积为0*/ }
	Point crosspoint(Line v)
	{
		double a1 = (v.e-v.s)^(s-v.s);
		double a2 = (v.e-v.s)^(e-v.s);
		return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
	}
};
struct halfplane:public Line
{
	double angle;
	halfplane(){}
	//`表示向量s->e逆时针(左侧)的半平面`
	halfplane(Point _s,Point _e)
	{
		s = _s;
		e = _e;
	}
	halfplane(Line v)
	{
		s = v.s;
		e = v.e;
	}
	void calcangle(){angle = atan2(e.y-s.y,e.x-s.x);}
	bool operator <(const halfplane &b)const{return angle < b.angle;}
};
struct polygon
{
	int n;
	Point p[maxp];
	Line l[maxp];
	double getarea()
	{
		double sum = 0;
		for(int i = 0;i < n;i++){
			sum += (p[i]^p[(i+1)%n]);
		}
		return fabs(sum)/2;
	}
};
struct halfplanes
{
	int n;//需要输入 
	halfplane hp[maxp];//需要输入,且封闭区域都在向量逆时针方向 
	Point p[maxp];
	int que[maxp];
	int st,ed;//队列的头尾指针,且下标从0开始,指向元素就是头和尾 
	void push(halfplane tmp){hp[n++] = tmp;}
	//去重
	void unique()
	{
		int m = 1;
		for(int i = 1;i < n;i++)
		{
			if(sgn(hp[i].angle-hp[i-1].angle) != 0)
				hp[m++] = hp[i];
			//去除极角相同的情况下,位置在右边(沿向量方向)的边 
			else if(sgn( (hp[m-1].e-hp[m-1].s)^(hp[i].s-hp[m-1].s) ) > 0)
				hp[m-1] = hp[i];
		}
		n = m;
	}
	bool halfplaneinsert()//判断半平面交是否存在 
	{
		for(int i = 0;i < n;i++)hp[i].calcangle();//计算每条边的倾斜角 
		sort(hp,hp+n);//先对倾斜角排序 
		unique();
		que[st=0] = 0;
		que[ed=1] = 1;
		p[1] = hp[0].crosspoint(hp[1]);
		for(int i = 2;i < n;i++){
			while(st<ed && sgn((hp[i].e-hp[i].s)^(p[ed]-hp[i].s))<0)ed--;
			while(st<ed && sgn((hp[i].e-hp[i].s)^(p[st+1]-hp[i].s))<0)st++;
			que[++ed] = i;
			if(hp[i].parallel(hp[que[ed-1]]))return false;
			p[ed]=hp[i].crosspoint(hp[que[ed-1]]);
		}
		while(st<ed && sgn((hp[que[st]].e-hp[que[st]].s)^(p[ed]-hp[que[st]].s))<0)ed--;
		while(st<ed && sgn((hp[que[ed]].e-hp[que[ed]].s)^(p[st+1]-hp[que[ed]].s))<0)st++;
		if(st+1>=ed)return false;//最后剩下小于三条直线,表明半平面交不存在 
		return true;
	}
	void getconvex(polygon &con)
	{
		p[st] = hp[que[st]].crosspoint(hp[que[ed]]);
		con.n = ed-st+1;
		for(int j = st,i = 0;j <= ed;i++,j++)
			con.p[i] = p[j];
	}
};

int u[maxp], v[maxp], w[maxp];

signed main()
{
	int n;
	cin >> n;
	for(int i = 1; i <= n; i++) 
		scanf("%d%d%d", &u[i], &v[i], &w[i]);
	for(int i = 1; i <= n; i++)//判断第i个人是否有可能win 
	{
		halfplanes h;
		h.n = 0;
		bool flag = true;
		for(int j = 1; j <= n; j++)
		{
			if(i == j)
				continue;
			double a = 1.0/u[i]-1.0/u[j];
			double b = 1.0/v[i]-1.0/v[j];
			double c = 1.0/w[i]-1.0/w[j];
			if(sgn(a) == 0 && sgn(b) == 0)
			{
				if(sgn(c) >= 0)
				{
					puts("No");
					flag = false;
					break;
				}
				else
					continue;
			}
			if(sgn(a) == 0)
			{
				if(b > 0)
					h.push(halfplane(Point(1, -c/b), Point(0, -c/b)));
				else
					h.push(halfplane(Point(0, -c/b), Point(1, -c/b)));
			}
			else if(sgn(b) == 0)
			{
				if(a > 0)
					h.push(halfplane(Point(-c/a, 0), Point(-c/a, 1)));
				else
					h.push(halfplane(Point(-c/a, 1), Point(-c/a, 0)));
			}
			else
			{
				if(b > 0)
					h.push(halfplane(Point(1, (-a-c)/b), Point(0, -c/b)));
				else
					h.push(halfplane(Point(0, -c/b), Point(1, (-a-c)/b)));
			}
		}
		//设置一个无穷大的正方形,方便求半平面交的面积 
		h.push(halfplane(Point(0, 0), Point(inf, 0)));
		h.push(halfplane(Point(inf, 0), Point(inf, inf)));
		h.push(halfplane(Point(inf, inf), Point(0, inf)));
		h.push(halfplane(Point(0, inf), Point(0, 0)));
		if(flag)
			if(h.halfplaneinsert())
			{
				polygon con;
				h.getconvex(con);
				if(con.getarea() > eps)
					puts("Yes");
				else
					puts("No");
			}
			else
				puts("No");
	}

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值