Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 8012 | Accepted: 2946 |
Description
Input
Output
The picture to the right below illustrates the first case from input.
Sample Input
5 1 1 4 2 2 3 3 1 1 -2.0 8 4 1 4 8 2 3 3 6 -2.0 3 0 0 1 1 1 0 2 1 2 0 3 1 0
Sample Output
Top sticks: 2, 4, 5. Top sticks: 1, 2, 3.
Hint
题目大意:
Stan 他有n根木棍,他从第一根木棍开始扔,一直扔到第n根,而且他每次告诉你他扔的木棍的两个端点(也就是确定这条线段)。问题要你输出没有被其他木棍覆盖在上面的是哪些棍子。
解题思路:
最后一根肯定没有被别的木棍覆盖,不和任何一根相交的木棍肯定也是答案,虽然和别的木棍相交,但是是这一堆里头最后一个丢的,也是答案。于是我们就从第一根木棍开始和2~n根棍子相比,如果第2~n根棍子有一根与第一根相交,那么第一根肯定不是,我们就用一个数组bool top[]来记录,top[1]=false。然后再将第二根和后面的所有进行比对,一直到n-1根。那么中心问题只要解决如何判断两个线段相交的问题了。我们要做两个判断
1)judge_range() :
当我们发现有一根线段的最下面的那个点比另外一根线段的最上面的那个端点还要高,也就是 min( segmeng1.a.y , segmeng1.b.y) > max ( segment2.a.y , segment2.b.y ) ,那么这两条线段是不可能相交的(我们仔细看看第四条和第五条就能发现)。
2)judge_cross() :
作两次叉乘判断,这两次叉乘必须均小于或者等于0,就说明这两条线段的端点是分别跨立在另外一条线段两边的(这个是原理性的东西,不懂的只能去看看书了)。
如果这两个判断都满足,我们就可以确定这两条线段是相交的。
代码如下:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
#define maxn 100008
#define eps 10e-9
struct Point
{
double x,y;
};
typedef Point Vector;
struct Segment
{
Point s,e;
}seg[maxn];
double cross ( Point a, Point b , Point o )
{
return (a.x-o.x) * ( b.y-o.y ) - ( a.y-o.y) * ( b.x - o.x );
}
bool judge_range ( Segment a , Segment b )
{
if ( min ( a.s.x , a.e.x ) > max ( b.s.x , b.e.x ) ||
min ( a.s.y , a.e.y ) > max ( b.s.y , b.e.y ) ||
min ( b.s.x , b.e.x ) > max ( a.s.x , a.e.x ) ||
min ( b.s.y , b.e.y ) > max ( a.s.y , a.e.y ) )
return false;
return true;
}
bool judge_cross ( Segment a, Segment b )
{
double t1,t2,t3,t4;
t1 = cross ( a.s , b.s , a.e );
t2 = cross ( a.s , b.e , a.e );
t3 = cross ( b.s , a.s , b.e );
t4 = cross ( b.s , a.e , b.e );
if ( ( t1 * t2 < eps ) && ( t3 * t4 < eps ) ) return true;
return false;
}
bool check_cross ( Segment a , Segment b )
{
if ( judge_range ( a , b ) && judge_cross ( a , b ) ) return true;
return false;
}
int n;
bool top[maxn];
int ans[1008];
int main()
{
while ( ~scanf ( "%d" , &n ) && n )
{
memset ( top , true , sizeof ( top ) );
for ( int i = 1 ; i <= n ; i ++ )
scanf ( "%lf %lf %lf %lf" , &seg[i].s.x , &seg[i].s.y , &seg[i].e.x , &seg[i].e.y );
for ( int i =1 ; i < n ; i ++ )
{
for ( int j = i + 1 ; j <= n ; j ++ )
if ( check_cross ( seg[i] , seg[j] ) )
{
top[i] = false;
break;
}
}
int cnt = 0;
for ( int i = 1 ; i <= n ; i ++ )
if ( top[i] ) ans[cnt++] = i;
printf ( "Top sticks:");
for ( int i = 0 ; i < cnt-1 ; i ++ )
printf ( " %d," , ans[i] );
printf ( " %d.\n" , ans[cnt-1] );
}
return 0;
}
技巧总结:
注意直线和线段相交 与 线段与线段相交是不一样的。线段之间的相交判断要复杂的多,因为线段规定了两个端点,它们的端点必须互相跨立在对方的两边才算相交。