POJ 1755 Triathlon 判断不等式有解+半平面的交

题目描述:http://poj.org/problem?id=1755

解题思路:

假设一个队员的各项速度为(v1,v2,v3),每项的距离为(x, y, z),那么她完成比赛的时间t = x/v1 + y/v2 + z/v3。

一名选手可以获得第一,当且仅当对于其他任意一名选手(v1', v2', v3'), t<t'有解。

即  x/v1+y/v2+z/v3 < x/v1'+y/v2'+z/v3' -> (1/v1-1/v1') x + (1/v2-1/v2') y + (1/v3-1/v3') z < 0 有解。

这个方程确定了一个三维半空间,因为x,y,z>0,所以左边除以z可以降到二维,就成为了一个半平面。

对于第i个选手,其余n-1个选手所确定的半平面如果有解则证明这名选手可以获得第一。

其他还有一些需要注意的地方。

1. 已知两点,求这两点确定的直线与给定方程的直线的交点。假设交点(x,y),因为(x1,y1)(x,y)(x2,y2)在同一直线上,利用叉乘可以列出一个方程。又因为(x,y)在给定直线方程上,联立可以解出(x, y)。

2. 看了discuss才知道eps要开到1e-16,不然一直WA..

3. 当两名队员的v1 v2 v3相等时,他们都不可能成为唯一的第一名,这一点需要特判。

<span style="font-family:Microsoft YaHei;font-size:14px;">/*
 * 2014.11.22
 * Problem: 1755	
 * Memory: 204K		Time: 0MS
 * Language: C++	Result: Accepted
 *
 */ 
#include "iostream"
#include "cmath"
#define EPS 1e-16 //!
#define MAXN 107
#define INF 1e10
#define zero(a) (fabs(a)<EPS)

struct Point {
	double x, y;
	Point(){}
	Point(double _x, double _y):x(_x),y(_y){}
};

struct Equation {
	double a, b, c;
	Equation(){}
	Equation(double _a, double _b, double _c):a(_a),b(_b),c(_c){}
}a[MAXN];

int n;

void init() {
	scanf("%d", &n);
	double v1, v2, v3;
	for (int i=1; i<=n; i++) {
		scanf("%lf %lf %lf", &v1, &v2, &v3);
		a[i].a = v1; a[i].b = v2; a[i].c = v3;
	}
}

double cross(Point a, Point b, Point c) {
	double x1 = b.x-a.x, y1 = b.y-a.y, x2 = c.x-a.x, y2 = c.y-a.y;
	return x1*y2 - x2*y1;
}

double area(Point p[], int pn) {
	double a = 0;
	for (int i=2; i<pn; i++) a += cross(p[1], p[i], p[i+1]);
	return a;
}

Point intersect(Point p1, Point p2, double a, double b, double c) {
	double d = p2.y-p1.y, e = p1.x-p2.x, f = p1.x*p2.y - p2.x*p1.y;
	c = -c;
	return Point((c*e-b*f)/(a*e-b*d), (c*d-a*f)/(b*d-a*e));
}

bool cut(Point c[], int &cn, double A, double B, double C) { // &cn!!
	Point t[MAXN];
	int tn = 0;
	for (int i=1; i<=cn; i++) {
		if (A*c[i].x+B*c[i].y+C<EPS) t[++tn] = c[i];
		else {
			if (A*c[i-1].x+B*c[i-1].y+C<-EPS) t[++tn] = intersect(c[i], c[i-1], A, B, C);
			if (A*c[i+1].x+B*c[i+1].y+C<-EPS) t[++tn] = intersect(c[i], c[i+1], A, B, C);
		}
	}
	if (zero(area(t, tn))) return false;
	for (int i=1; i<=tn; i++) c[i] = t[i];
	c[0] = c[cn=tn]; c[cn+1] = c[1];
	return true; 
}

bool planeIntersect(Equation e[], int n) {
	Point c[MAXN];
	int cn;
	c[1] = Point(EPS, EPS); c[2] = Point(EPS, INF); c[3] = Point(INF, INF); c[4] = Point(INF, EPS);
	cn = 4; c[0] = c[cn]; c[cn+1] = c[1];
	for (int i=1; i<=n; i++) {
		if (zero(e[i].a)&&zero(e[i].b)&&zero(e[i].c)) return false;
		if (!cut(c, cn, e[i].a, e[i].b, e[i].c)) return false;
	}
	return true;
}

int main() {
	init();
	Equation ln[MAXN];
	int lnn;
	for (int i=1; i<=n; i++) {
		lnn = 0;
		for (int j=1; j<=n; j++) 
			if (i!=j) ln[++lnn] = Equation((a[j].a-a[i].a)/(a[i].a*a[j].a),
			                               (a[j].b-a[i].b)/(a[i].b*a[j].b), 
			                               (a[j].c-a[i].c)/(a[i].c*a[j].c));
		if (planeIntersect(ln, lnn)) {
			printf("Yes\n");
		} else {
			printf("No\n");
		}
	}

	return 0;
}
	</span>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值