poj1039

题目较难,在lrj书P359,

题意大致为:

有一宽度为1的折线管道,上面顶点为(xi,yi),所对应的下面顶点为(xi,yi-1),假设管道都是不透明的,不反射的,光线从左边入口处的(x1,y1),(x1,y1-1)之间射入,向四面八方传播,求解光线最远能传播到哪里(取x坐标)或者是否能穿透整个管道.

分析如下:上下顶点对于限制光线十分重要。首先:我们知道如果一条光线始终没有接触到一个顶点,那么它肯定不是最优的,可以通过平移使之接触到一个顶点,且更优化。其次,又可以推知,如果一条光线只接触到一个顶点,那么其也必定不是最优的,可以通过旋转使之接触两个顶点。且更优化。且这两个顶点必定是一个上顶点和一个下顶点。

分析到这里,思路就很明确了。 枚举任意上顶点和下顶点,构成一条直线。若该直线在x等于x0的情况下y值在down与up之间,则说明是一条”正确“的直线。那么接下来就是判断每段管道的上下层是否与该直线相交,直到规范相交为止,然后求出交点横坐标。最后求出每条正确直线的交点横坐标的最大值,即为题意要求的值。

下面是代码: 156K+47MS

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define eps 1e-3 //精度,实际可更大,最好为1e-6
#define inf 99999.0 //极大值,可更大
#define Max 30 
#define Maxx(a,b) (a)>(b)?(a):(b) //注意宏最好单独使用,本题就是与<共同使用结果WA,混用的结果常常不可预知
typedef struct Point{ // 点坐标
	double x;
	double y;
}point;
int n;
point up[Max]; // 上方顶点
point down[Max]; // 下方顶点
int dblcmp(double p){ // 浮点数判断正负零模板
	if(fabs(p)<eps)
		return 0;
	return p>0?1:-1;
}
double det(double x1,double y1,double x2,double y2){ // 叉积模板
	return x1*y2-x2*y1;
}
double cross(point A,point B,point P){ // 判断p在直线AB左侧还是右侧,螺旋关系
	return det(B.x-A.x,B.y-A.y,P.x-A.x,P.y-A.y); 
}
bool check(point A,point B,point C,point D){ // 判断直线AB与线段CD是否非规范相交,若规范相交则要去掉=
	return dblcmp(cross(A,B,C))*dblcmp(cross(A,B,D))<=0;
}
double intersection(point A,point B,point C,point D){ // 求直线AB与线段CD的交点
	double area1=cross(A,B,C);
	double area2=cross(A,B,D);
	double c=dblcmp(area1),d=dblcmp(area2);
	if(c*d<0) // 若为规范相交
		return (area2*C.x-area1*D.x)/(area2-area1); // 交点横坐标
	if(c*d==0){ // 若为非规范相交
		if(c==0) // 若C在直线AB上,则交点坐标即为C的横坐标
			return C.x;
		else // 否则为D点横坐标
			return D.x;
	} // 若不相交,则返回负无穷,便于主程序一致处理
	return -inf;
}
int main(){
	while(scanf("%d",&n),n){
	    int i,j,k;
		for(i=1;i<=n;i++){ // 输入管道坐标
			scanf("%lf%lf",&up[i].x,&up[i].y);
			down[i].x=up[i].x,down[i].y=up[i].y-1;
		}
		bool flag=false; // 标记是否为贯穿整个管道
		double max_x=-inf; // 初始化最大横坐标为负无
		for(i=1;i<=n;i++){
			for(j=1;j<=n;j++){
				if(i!=j){
					for(k=1;k<=n;k++)
						if(!check(up[i],down[j],up[k],down[k])) //检查规范相交的折线段
							break;
					if(k>n){ // 若无规范相交,则说明贯穿整个管道
						flag=true; //置标记为true
						break; // 跳出j循环
					}
					int what=Maxx(i,j); //取i,j中的最大值
					if(k>what){ // 若k小于或等于i与j中的最大值,则说明该直线不是正确的直线,否则为正确直线
						double temp=intersection(up[i],down[j],up[k],up[k-1]); //因无法判断是上面还是下面规范相交,故可取最大值,不影响结果
						if(max_x<temp) //取大值
							max_x=temp;
						temp=intersection(up[i],down[j],down[k],down[k-1]); //假设与下面管道相交
						if(max_x<temp) // 取大值
							max_x=temp;
					}
				}
			}
			if(flag) // 若贯穿整个管道,则跳出i循环
				break;
		}
		if(flag) // 若贯穿整个管道
			printf("Through all the pipe.\n");
		else // 否则取最大横坐标
			printf("%.2lf\n",max_x);
	}
	return 0;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值