题目来源:http://poj.org/problem?id=4048

 

 

Problem E. Chinese Repeating Crossbow
Description
In Chinese history, Zhuge Liang, prime minister of Shu in three kingdoms period,
is regarded as the embodiment of wisdom. When he was dying he passed a book to
his apprentice Jiang Wei privately. This book recorded the introduction and
specification of a most powerful weapon at that time, called Chinese repeating
crossbow or Zhuge repeating crossbow (Figure 1). This weapon can shoot many
arrows in a very short time and makes enemies very hard to defense it.

Figure 1 Chinese Repeating Crossbow
Later on, Jiang Wei built a repeating crossbow according to the book and he
wanted to know exactly about its power. He came to the center of a test field. The test
field was ragged with several extreme high straw walls. Jiang Wei wanted to choose a
direction, shot through as many straw walls as he can by his new weapon.
The problem given to you is quite simple: assuming that the repeating crossbow
can shot through any numbers of straw walls, please help Jiang Wei to choose a
certain direction that he can shot through maximum number of walls in one shot. The
walls can be considered as line segments, and if an arrow touches the end points of a
wall, it's also called "shoot through". The straw walls can intersect or overlap with
each other, and Jiang Wei may possibly stand extremely close to one of the straw wall.
Input
The first line of the input contains an integer T (T<=10) representing the number
of test cases.
In each test case, the first line contains an integer N (0<N<=1500) representing
the number of straw walls on the field. Each of the following N lines contains four
integer numbers x1, y1, x2, y2,which describes the end point (x1, y1) and (x2, y2) of a
straw wall. The last line of the test case contains two integer (x, y) representing the
position of Jiang Wei. All the coordinates are integer numbers and in the range of
[-10000, 10000].
Output
For each test case, output a single integer representing the maximum number of straw walls can be shot through in one shot.
Sample Input
2
3
-1 -1 1 -1
-1 1 1 1
-1 2 1 2
0 0
3
-1 -1 1 -1
-1 0 0 1
1 0 0 1
0 0
Sample Output
2
2

 

分析: 求与线段相交最多交点的射线一定 经过线段的某端点。 求该射线与线段的交点 转换为 求 线段与线段的交点, 即 将射线 转换成 线段。通过斜率,

确定射线另一端端点C 的坐标。

设固定点为 A, 射线的经过的一个线段端点为 B ,  分两种情况:

1: AB 斜率不存在 ,即 若A.x == B.x  。

    若B.x> A.x  则设 另一端点C 的坐标为(A.x, 10008), 

    若B.x<A.x  则设 另一端点C 的坐标为(A.x, -10008), 

2:AB 斜率存在,  k= (B.y-A.y) / (B.x-A.x)

    若B.x> A.x 则设 另一端点C的坐标为 (10008, A.y+ k ( 10008 - A.x))

    若B.x<A.x ,则另一端点C的坐标为 (-10008, A.y+k(-10008 - A.x))

由于斜率是  double ,故需要考虑精度问题,虽然输入输出为 int,我们用 double 处理数据。

代码如下:

#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <stack>
#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <set>
#include <math.h>
#include <cmath>
#include <map>
#include <stack>
#include <queue>

using namespace std ;

double EPS=1e-10;
// 考虑误差的加法运算
double add(double a,double b)
{
    if(fabs(a+b)<EPS*(fabs(a)+fabs(b))) return 0;
    return a+b;
}
struct Point{
    double x,y;
    Point(){}
    Point(double x,double y):x(x),y(y){} // 构造函数,方便代码编写
    Point operator +(Point p){
        return Point(add(x,p.x), add(y,p.y));
    }
    Point operator-(Point p){
        return Point(add(x,-p.x),add(y,-p.y));
    }
    Point operator*(double d){
        return Point(x*d,y*d);
    }
    double operator*(Point p){  // 内积 点乘
        return add(x*p.x, y*p.y);
    }
    double operator^(Point p){//  外积 叉乘
        return add(x*p.y,-y*p.x);
    }
};
//判断点p0是否在线段p1p2内
int on_segment(Point p1, Point p2, Point p0)
{
    if (((p1-p0).x * (p2-p0).x <=0 )&& ((p1-p0).y * (p2-p0).y <=0))   // 中间是 &&
        return 1;
    return 0;
}
// 判断线段p1p2与q1是否相交
int intersection(Point p1,Point p2, Point q1,Point q2)
{
    double d1=(p2-p1)^(q1-p1);           // 计算p1p2 到q 点的转向 d1>0 左转,  d1 <0 右转
    double d2=(p2-p1)^(q2-p1);
    double d3=(q2-q1)^(p1-q1);
    double d4=(q2-q1)^(p2-q1);
    if((d1==0 && on_segment(p1,p2,q1) )|| (d2==0 && on_segment(p1,p2,q2) )||
       (d3==0&& on_segment(q1,q2,p1)) || (d4==0 && on_segment(q1,q2,p2)))
       return 1;
    else if(d1*d2<0 && d3*d4 <0)    // 中间是 &&
        return 1;
    return 0;
}

struct Line{
       Point st ;
       Point ed ;
       Line(){}
       Line(Point a , Point b){
           st = a ;
           ed = b ;
       }
       void read(){
            scanf("%lf%lf%lf%lf" , &st.x , &st.y , &ed.x , &ed.y) ;
       }
};

const double Max_p  = 10008 ;
Line Getshexian(Point A , Point B){
     if(A.x == B.x){
          if(B.y >= A.y)
              return Line(A , Point(A.x , Max_p)) ;
          else
              return Line(A , Point(A.x ,-Max_p)) ;
     }
     if(B.x > A.x){
           double k = (B.y - A.y) / (B.x - A.x)  ;
           double y = (Max_p -A.x)* k + A.y ;
           return Line(A , Point(Max_p , y)) ;
     }
     else{
           double k = (B.y - A.y) / (B.x - A.x)  ;
           double y = (-Max_p -A.x)* k + A.y ;
           return Line(A , Point(-Max_p , y)) ;
     }
}

Line line[1508] ;

int n ;

int Ans(Line she){
    int i , ans = 0 ;
    for(i = 1 ; i <= n ; i++){
         if(intersection(she.st , she.ed , line[i].st  , line[i].ed))
              ans++ ;
    }
    return ans ;
}

int main() {
    int i , ans , t,sum ;
    Point A ;
    cin>>t ;
    while(t--){
         scanf("%d" ,&n) ;
         for(i = 1 ; i <= n ; i++)
             line[i].read() ;
         scanf("%lf%lf" ,&A.x ,&A.y) ;
         ans = 0 ;
         for(i = 1 ; i <= n ; i++){
             Line now = Getshexian(A , line[i].st) ;
             ans = max(ans , Ans(now)) ;
             now = Getshexian(A , line[i].ed) ;
             ans = max(ans , Ans(now)) ;
         }
         cout<<ans<<endl ;
    }
    return 0;
}