您的位置:首页 > 其它

poj 1039

2016-07-25 21:53 399 查看

题目概述

有一种管线,横截面是上下平行的两根折线,在坐标系中,两根折线上横坐标相等的点,其纵坐标相差1,管线不透光,不反光,但拐点处不会阻挡光,现有一束光从管线最左侧开口处射入,给定管线上部各点坐标,问光能否贯穿整条管线,若不能,求出其最远到达位置的横坐标

时限

1000ms/3000ms

输入

第一行正整数N,代表管线上部共N个端点,其后N行,每行两个浮点数,描述管线上部各端点位置,输入到N=0结束

限制

2<=N<=20

输出

每组数据输出在一行中,若可贯穿,则输出字符串

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

4

-7 1

-5 2

-3 1

-1 4

0

样例输出

4.67

Through all the pipe.

-2.33

讨论

计算几何,直线与线段位置关系,首先一定要注意,题目可没说横坐标不能全是负的,额初始化most=0.0直接贡献一WA,主要的实现,看过刘汝佳黑书的应该能知道(额没看过,看别人的解法才知道),走的最远的光线一定经过上下折线各一个端点,这和poj 3304有异曲同工之妙,并且判断最远位置时,仅需要判断是否和某上下一对端点所成线段相交即可,若不相交,则求光线与这对端点左侧的那两条线段的交点横坐标,取较大者即可,结合上面两条,再加上一点,光肯定是要经过起点的,如果都无法经过最左侧两个端点所成线段,那又哪来的光呢,这样基本思路也就明晰了

判断是否和上下端点所成线段相交是一个非常明智的转化做法,因为题目说端点并不会阻挡光,但若直接枚举每条线段则必然要判断光是擦过拐点还是撞上管壁,这需要端点前后两条线段分别和光的端点做向量积得到(在同侧则撞上,在异侧则擦过),但最后一对端点都没有下一条线段,因而还需要特殊处理,仅仅考虑至此,代码已经非常复杂了,其实还有其他一些小地方也需要处理,就不再枚举了

题解状态

192K,47MS,C++,1777B

题解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 41
#define memset0(a) memset(a,0,sizeof(a))
#define EPS 1e-6

int N;//端点数的一半
double x[MAXN], y[MAXN];//端点横纵坐标
double xp(double x1, double y1, double x2, double y2, double x3, double y3)
{
return (x1 - x2)*(y3 - y2) - (y1 - y2)*(x3 - x2);
}
bool intersect(double x1, double y1, double x2, double y2, double x3, double y3, double<
d69c
/span> x4, double y4)
{
return xp(x3, y3, x1, y1, x2, y2)*xp(x4, y4, x1, y1, x2, y2) <= EPS;
}//直线与线段相交的两大函数 在之前的题中介绍过无数次了
double point_of_intersection_x(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)//交点横坐标 由于只求横坐标 因而没有斜率的情况方便不少
{
if (abs(x1 - x2) < EPS)
return x1;
if (abs(x3 - x4) < EPS)
return x3;
else {
double k1 = (y2 - y1) / (x2 - x1);
double k3 = (y4 - y3) / (x4 - x3);
return ((y3 - y1) + k1*x1 - k3*x3) / (k1 - k3);
}
}
void fun()
{
for (int p = 0; p < N; p++) {
scanf("%lf%lf", &x[p], &y[p]);//input
x[N + p] = x[p], y[N + p] = y[p] - 1;//下部的从下标N开始
}
double most = -INF;//详见第三组样例数据
for (int p = 0; p < N; p++)//枚举一个上点
for (int i = N; i < 2 * N; i++) {//枚举一个下点
if (!intersect(x[p], y[p], x[i], y[i], x[0], y[0], x
, y
))
continue;//没过起点 没戏
bool f = 1;//光没有与任何管壁X型相交的标记
for (int u = 1; u < N; u++) {//枚举上下一对的点所成线段
if (!intersect(x[p], y[p], x[i], y[i], x[u], y[u], x[u + N], y[u + N])) {//遇到没有相交的
most = max(most, point_of_intersection_x(x[p], y[p], x[i], y[i], x[u], y[u], x[u - 1], y[u - 1]));//求与上部交点并取最大值
most = max(most, point_of_intersection_x(x[p], y[p], x[i], y[i], x[u + N], y[u + N], x[u - 1 + N], y[u - 1 + N]));//求与下部交点并取最大值
f = 0;//与管壁相撞过了
break;//光不会再前进了
}
}
if (f) {//如果光直接贯穿管线
printf("Through all the pipe.\n");//output
return;
}
}
printf("%.2lf\n", most);//output
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);

while (~scanf("%d", &N) && N) //input
fun();
}


EOF
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: