poj 2165 Gunman

Gunman
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 1351 Accepted: 492 Special Judge

Description

Consider a 3D scene with OXYZ coordinate system. Axis OX points to the right, axis OY points up, and axis OZ points away from you. There is a number of rectangular windows on the scene. The plane of each window is parallel to OXY , its sides are parallel to OX and OY . All windows are situated at different depths on the scene (different coordinates z > 0). 

A gunman with a rifle moves along OX axis (y = 0 and z = 0). He can shoot a bullet in a straight line. His goal is to shoot a single bullet through all the windows. Just touching a window edge is enough. 
Your task is to determine how to make such shot.

Input

The first line of the input file contains a single integer number n (2 <= n <= 100) — the number of windows on the scene. The following n lines describe the windows. Each line contains five integer numbers x 1i, y 1i, x 2i, y 2i, z i (0 < x 1i, y 1i, x 2i, y 2i, z i < 1000). Here (x 1i, y 1i, z i) are coordinates of the bottom left corner of the window, and (x 2i, y 2i, z i) are coordinates of the top right corner of the window (x 1i < x 2i, y 1i< y 2i). Windows are ordered by z coordinate (z i > z i-1 for 2 <= i <= n).

Output

Output a single word "UNSOLVABLE" if the gunman cannot reach the goal of shooting a bullet through all the windows. 
Otherwise, on the first line output a word "SOLUTION". On the next line output x coordinate of the point from which the gunman must fire a bullet. On the following n lines output x, y, z coordinates of the points where the bullet goes through the consecutive windows. All coordinates in the output file must be printed with six digits after decimal point.

Sample Input

3
1 3 5 5 3
1 2 5 7 5
5 2 7 6 6

Sample Output

SOLUTION
-1.000000
2.000000 3.000000 3.000000
4.000000 5.000000 5.000000
5.000000 6.000000 6.000000

Source

提示

题意:

在三维坐标内有许多窗子,给出窗子大小以及位置,保证第一扇窗子要比第二扇远,一个枪手要想让子弹穿过全部窗子,请你为他在x轴选择一个合适的点从而实现他的目标。如果存在请输出“SOLUTION”,之后输出点的位置,接着输出子弹轨迹在窗子上的坐标,擦边也算穿过。不存在输出“UNSOLVABLE”。(答案不唯一)

思路:

三维问题转化为XOZ面和YOZ面问题。

先看XOZ面,假设所有窗子的y坐标为0,也就是投影,那么这就类似于POJ1039Pipe,运用模板枚举所有线段的段点,求出通过所有投影直线的方程。

之后看YOZ面,也是假设所有窗子的x坐标为0,这时直线的投影是通过原点的,根据y=kz来判断能否通过所有线段(窗子投影),比如y1是的下边,y2是窗子的上边,k1=y1/z,k2=y2/z,且利用k1,k2看直线有没有与线段相交。

如果两条都符合说明一定至少有1条直线成立,那么在XOZ面的直线与x轴相交的点就是答案,也可以求出直线与窗子相交的x值,在YOZ面求出的k值用来求出直线与窗子相交的y值,z值就是窗子的坐标。

示例程序

Source Code

Problem: 2165		Code Length: 2040B
Memory: 408K		Time: 32MS
Language: GCC		Result: Accepted
#include <stdio.h>
#include <math.h>
struct point
{
    double x,y,z;
}p[100][2];
struct
{
    double A,B,C;		//直线方程是Ax+Bz+C=0
}line;
double k;
double cross(struct point a,struct point b,struct point o)
{
    return (a.x-o.x)*(b.z-o.z)-(b.x-o.x)*(a.z-o.z);
}
int f(struct point a,struct point b,int n)
{
    int i;
    double t;
    for(i=0;n>i;i++)
    {
        t=cross(p[i][0],b,a)*cross(p[i][1],b,a);		//利用叉乘判断相交
        if(t>=0&&fabs(t)>=1e-10)
        {
            return 0;
        }
    }
    line.A=b.z-a.z;
    line.B=a.x-b.x;
    line.C=a.z*b.x-a.x*b.z;
    return 1;
}
int check(int n)
{
    int i,i1,i2;
    for(i=0;n-1>i;i++)
    {
        for(i1=i+1;n>i1;i1++)
        {
            if(f(p[i][0],p[i1][0],n)==1)
            {
                return 1;
            }
            if(f(p[i][0],p[i1][1],n)==1)
            {
                return 1;
            }
            if(f(p[i][1],p[i1][0],n)==1)
            {
                return 1;
            }
            if(f(p[i][1],p[i1][1],n)==1)
            {
                return 1;
            }
        }
    }
    return 0;
}
int check1(int n)
{
    int i,i1,i2;
    double t;
    for(i=0;n>i;i++)
    {
        for(i1=0;2>i1;i1++)
        {
            k=p[i][i1].y/p[i][0].z;
            for(i2=0;n>i2;i2++)
            {
                if(k*p[i2][0].z-p[i2][1].y>1e-10||k*p[i2][0].z-p[i2][0].y<-1e-10)	//判断是否相交
                {
                    break;
                }
            }
            if(n==i2)
            {
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int n,i;
    scanf("%d",&n);
    for(i=0;n>i;i++)
    {
        scanf("%lf %lf %lf %lf %lf",&p[i][0].x,&p[i][0].y,&p[i][1].x,&p[i][1].y,&p[i][0].z);
        p[i][1].z=p[i][0].z;
    }
    if(check(n)==1&&check1(n)==1)			//第一个是XOZ面的判断,第二个是YOZ面
    {
        printf("SOLUTION\n%.6f\n",-line.C/line.A);
        for(i=0;n>i;i++)
        {
            printf("%.6f %.6f %.6f\n",-(p[i][0].z*line.B+line.C)/line.A,k*p[i][0].z,p[i][0].z);
        }
    }
    else
    {
        printf("UNSOLVABLE");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值