POJ 1039 Pipe (计算几何、思维、线段相交)

题目链接:http://poj.org/problem?id=1039


题意:给一条曲折的管道,给出的形式是每一个转弯处口的坐标,求光线从入口进入能到的最远的x点,如果能穿过管子则输出“Through all the pipe.”


输入:

4
0 1
2 2
4 1
6 4
6
0 1
2 -0.6
5 -4.45
7 -5.57
12 -10.8
17 -16.55
0

每个样例第一行n表示n个折点,下面n行。


参考博客:http://blog.csdn.net/lin375691011/article/details/17800529


首先要知道:
1、可以通过两向量的叉积来判断是否相交。具体见《算法艺术与信息学竞赛》P348开始向后很多页。
2、一条光线从入口到某一点必然会擦过一个上点(管转弯口上壁的点)和一个下点(管转弯口下壁的点)。
3、如果一条光线自始至终未擦到任何定点,这条光线是可以“优化的”。
4、“优化”就是可以通过旋转使它擦过一个上点和一个下点。
5、2、3、4点详见《算法艺术与信息学竞赛》P359.


解题思路:
1、先枚举光线是否能从入口射入,如果能判断能从第一个转弯处开始能穿过几个(判断能与多少个转弯处所在的竖直直线相交)。
2、如果遇到某一个转弯处穿不过了,计算入口到相交管壁的距离,有可能是上管壁,也有可能是下管壁,保存最大值。
3、如果能穿过所有的转弯处,就直接输出“Through all the pipe.”,否则输出最长距离。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
const double eps = 1e-8;
const double PI = acos(-1.0);
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;  
	}  
	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;  
	}  
	void transXY(double B) {   
		double tx = x,ty = y;   
		x = tx*cos(B) - ty*sin(B);   
		y = tx*sin(B) + ty*cos(B);  
	} 
};

struct Line {  
	Point s,e; 
	Line(){}  
	Line(Point _s,Point _e)  {
	   s = _s;e = _e;  
	} 
	pair<int,Point> operator &(const Line &b)const {
		Point res = s;
		if(sgn((s-e)^(b.s-b.e)) == 0) {
			if(sgn((s-b.e)^(b.s-b.e)) == 0) return make_pair(0,res);             
			else return make_pair(1,res);         
		} 
        double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
		res.x += (e.x-s.x)*t;         
		res.y += (e.y-s.y)*t;         
		return make_pair(2,res);     
	} 
};

double dist(Point a,Point b) {     
	return sqrt((a-b)*(a-b)); 
}

bool inter(Line l1,Line l2) {     
    return       
    max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&       
    max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&       
    max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&       
    max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&       
    sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0 &&       
    sgn((l1.s-l2.e)^(l2.s-l2.e))*sgn((l1.e-l2.e)^(l2.s-l2.e)) <= 0;   
} 

bool Seg_inter_line(Line l1,Line l2) {  //????l1?????l2??   
    return sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0;  
}
Point upp[25], downp[25];
int main() {
	int n;
	while(~scanf("%d", &n), n) {
		int i, j, k;
		for(i = 0; i < n; i++) {
			scanf("%lf %lf", &upp[i].x, &upp[i].y);
			downp[i] = upp[i];
			downp[i].y -= 1;
		}
		Line start(upp[0], downp[0]);
		Line end(upp[n - 1], downp[n - 1]);
		double ans = upp[0].x;
		int isthrough = 1;
		for(i = 0; i < n; i++) {
			for(j = 0; j < n; j++) {  //枚举所有上下折点 
				if(i == j) continue; 
				isthrough = 1;
				int isin = 1;
				Line l(upp[i], downp[j]);
				for(k = 0; k < j; k++)  {  //看是否能从入口射入 
					Line tl(upp[k], downp[k]);
					if(!Seg_inter_line(l, tl)) {
						isin = 0;
						isthrough = 0;  //此处别忘清零。 
						break;
					}
				}
				if(!isin) continue;
				for(k = j + 1; k < n; k++) {  //看能射到多远 
					Line tl(upp[k], downp[k]);
					if(!Seg_inter_line(l, tl)) {
						isthrough = 0;
						pair<int, Point> o = (l & tl);
						pair<int, Point> s = (start & l);
						Line ttl;
						if(o.second.y > upp[k].y) {
							ttl = Line(upp[k], upp[k - 1]);
						}
						else {
							ttl = Line(downp[k], downp[k - 1]);
						}
						pair<int, Point> e = (ttl & l);
						ans = max(ans, e.second.x);
						break;
					}
				}
				if(isthrough) break;
			}
			if(isthrough) break;
		}
		if(isthrough) puts("Through all the pipe.");
		else printf("%.2lf\n", ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值