题目链接:POJ - 2653
首先这题数据n<100000可能没错,但只要边添加,边判断弹出的话,还是可以直接用双for来判断。
1.边判断弹出怎么实现?
链表似乎是很不错的,从前往后遍历,不行的O(1)弹出,但不管是手写指针链表,stl链表,还是数组链表都麻烦些(还是不太会QAQ)。可以用两个队列来实现,每次弹空一个,判断是否压入另一个,注意下最后输出答案是从小到大的
2.判断线段相交呢?
先判断是否平行
重合的话返回YES,平行的话返回NO
不平行的话
当做判断直线和线段相交,只不过判定两遍,两个线段分别做一次直线和线段,如果两次直线和线段都相交返回YES(这个方法这题是没问题,仔细想想好像没什么问题,甚至上面的平行都不用判断)
否则返回NO
代码
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<utility>
#include<vector>
#include<queue>
#include<cmath>
using namespace std;
#define fo(a,b) for(int i=a;i<=b;i++)
#define inf 0x3f3f3f3f
#define ll long long
#define dou double
#define EXP 1e-6
#define M 1000005
int n;
struct P{
double x,y;
P(double x=0,double y=0):x(x),y(y){}
};
struct L{
P a,b;
int id;
L(){}
L(P a,P b):a(a),b(b){}
}cnt;
queue<L>q[2];
double area(P a,P b,P c){
return fabs((b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y))/2;
}
bool par(L a,L b){ //判断线段是否平行(重合和平行)
return fabs((a.b.x-a.a.x)*(b.b.y-b.a.y)-(b.b.x-b.a.x)*(a.b.y-a.a.y))<EXP;
}
dou cross(P mark,P a,P b){ //在向量a->b的左边
return (a.x-mark.x)*(b.y-mark.y)-(b.x-mark.x)*(a.y-mark.y);
}
int inte(L x,P a,P b)
{
return cross(a,x.a,x.b)*cross(b,x.a,x.b)<EXP;
}
int f(L x,L y){
if(par(x,y)){ //平行
if(area(y.a,x.a,x.b)>EXP) return 1; //平行
return 0; //重合
}
return !(inte(x,y.a,y.b)&&inte(y,x.a,x.b));
}
void add(L x,int d){
while(!q[d%2].empty()){
if(f(q[d%2].front(),x)) q[(d+1)%2].push(q[d%2].front());
q[d%2].pop();
}
q[(d+1)%2].push(x);
}
int main(){
while(cin>>n&&n){
fo(1,n){
cin>>cnt.a.x>>cnt.a.y>>cnt.b.x>>cnt.b.y;
cnt.id=i;
add(cnt,i);
}
printf("Top sticks:");
int now=(n+1)%2;
while(!q[now].empty()){
printf(" %d%c",q[now].front().id,q[now].size()==1?'.':',');
q[now].pop();
}
printf("\n");
}
return 0;
}