poj 1755 Triathlon 半平面交判断不等式是否有解

题意:n人参加铁人三项,已知每个人三段各自的速度ui,vi,wi,可以改变3段比赛的路程判断每个人是否能最先到达终点。
题解:半平面交可以判断不等式方程组是否有解。
首先构造方程组,设三段路程分别为s1,s2.对于第i个人,他所用时间 ti= s1/u1 + s2/vi+ s3/wi。所以对于第j个人,若要赢,则 s1( 1/uj - 1/ui ) + s2( 1/vj - 1/ui ) + s3( 1/wj - 1/wi)<0,所以可是设x=s1/s3 ,y=s2/s3,可以构造出 ax+by+c<0的方程组。因为x,y>0,所以将开始的界限设在第一象限。
至于为什么半平面交能判断不等式方程组是否有解。详情请见:我还没写的博客。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#define N 110
#define eps 1e-8
#define inf 0x3f3f3f3f3f3f
using namespace std;
struct node{
	double u,v,w;
}k[N];
struct point{
	double x,y;
	point(){}
	point(double a,double b){x=a,y=b;}
    point operator - (const point &t)const{
        return point(x-t.x, y-t.y);
    }
    double operator *(const point &t)const{
        return x*t.x + y*t.y;
    }
    double operator ^(const point &t)const{
        return x*t.y - y*t.x;
    }
}p[N];
double dist(point a,point b)
{
	return sqrt((a-b)*(a-b));
}
int n,pnum;
point q[N];
int top=0;
point cross_p(point x,point y,double a,double b,double c)//求两个点x,y和该线的交点
{
	point ans;
	double a2=y.y-x.y;
	double b2=x.x-y.x;
	double c2=x.y*y.x-y.y*x.x;//连接x,y的线段的a,b,c
	ans.y=(a*c2-a2*c)/(a2*b-a*b2);
	ans.x=(b*c2-b2*c)/(b2*a-b*a2);
	return ans;
}
void Hpi(double a,double b,double c)
{
	top=0;
	for(int i=1;i<=pnum;i++)
	{
		if(a*p[i].x+b*p[i].y+c<=eps)//如果上一个边界点满足,则记录
		{
			q[++top]=p[i];
		}
		else
		{
			if(a*p[i-1].x+b*p[i-1].y+c<=eps)//该节点不满足,但上一节点满足
			{
				q[++top]=cross_p(p[i-1],p[i],a,b,c);//加入两个节点的连线与该线的交点
			}
			if(a*p[i+1].x+b*p[i+1].y+c<=eps)//该节点不满足,但下一节点满足
			{
				q[++top]=cross_p(p[i],p[i+1],a,b,c);
			}
		}
	}
	for(int i=1;i<=top;i++) p[i]=q[i];//将更新好的点集返回p
	p[top+1]=p[1],p[0]=p[top];
	pnum=top;
}
bool solve(int id)
{
	p[1]=point(0,0);
	p[2]=point(inf,0);
	p[3]=point(inf,inf);
	p[4]=point(0,inf);
	p[0]=p[4],p[5]=p[1];
	pnum=4;//初始边界为第一象限四个边界点,所以起始点为4个
	double a,b,c;
	for(int i=1;i<=n;i++)
	{
		if(i!=id&&k[id].u<=k[i].u&&k[id].v<=k[i].v&&k[id].w<=k[i].w)
			return 0;//如果有人3项多比他强,肯定达不到
		if(i==id) continue;
		a=(k[i].u-k[id].u)/(k[id].u*k[i].u);
		b=(k[i].v-k[id].v)/(k[id].v*k[i].v);
		c=(k[i].w-k[id].w)/(k[id].w*k[i].w);
		Hpi(a,b,c);
		if(pnum<3) return 0;//如果最后交点数<3,即无法构成平面
	}
	return pnum>=3;
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lf%lf%lf",&k[i].u,&k[i].v,&k[i].w);
	for(int i=1;i<=n;i++)
	{
		if(solve(i)) printf("Yes\n");
		else printf("No\n");
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值