[题解]UVA10902 Pick-up Sticks

UVA10902 Pick-up Sticks

题意简述

多测。给定坐标系上依次给定\(n\)根木棍的起始和终止坐标,按顺序放置这些木棍,询问最终处在最上层的木棍有哪些。

\(n\le 100000\)。保证任意时刻最上层的木棍不超过\(1000\)个。

思路分析

看起来数据范围很刁钻,不过除了暴力以外的方法想不出了,就写了一份上交,结果过了。

思路就是用链表记录最上层的木棍,每放一根木棍就遍历链表,如果有相交的就把这个木棍从链表中移出去。

遍历完链表,再把当前木棍加进去。

难点主要在于如何判断木棍相交。

在平面上判断两线段相交,需要同时使用“快速排斥实验”与“跨立实验”。

所谓快速排斥实验,就是先粗略地判断一下两线段支起的矩形是否相交。如果两个矩形都不相交,两线段肯定也不相交。

但仅仅矩形相交显然不能说线段相交。还需要进行跨立实验。

从线段\(1\)的一端向线段\(2\)的两端作\(2\)个向量。记录红色向量和橙色向量的方向关系为\(A\)(顺时针/逆时针/共线)。
再从线段\(1\)的另一端向\(2\)的两端作\(2\)个向量。记方向关系为\(B\)
再用线段\(2\)向线段\(1\)重复上面的过程,记方向关系为\(C,D\)
如果\(A\)\(B\)都是顺时针或者都是逆时针,或者\(C,D\)都是顺时针或者都是逆时针,则说明\(1\)条线段的两个端点位于另一条线段所在直线的一端,自然两线段不相交。
否则说明\(1\)条线段的两个端点位于另一条线段所在直线的两端,又因为我们进行了快速排斥实验,保证了如果在另一条线段所在直线的两端,两线段一定相交。

注意到两向量可能出现共线的情况,这说明一定是某一点在一条线段上了。这种情况仍然是相交,所以不用参与判断。

跨立实验只能判断线段与直线的关系,配合快速排斥实验就可以判断线段与线段之间的关系了。

至于如何判断两向量的方向关系,就是用叉积了。两向量\(\vec{a}=(x_1,y_1),\vec{b}=(x_2,y_2)\)的叉积是一个向量,其长度为\(\vec{a}\times \vec{b}\)\(x_1\times y_2-x_2\times y_1\)。该值:
\(>0\)\(\vec{a}\)\(\vec{b}\)的顺时针方向。
\(<0\)\(\vec{a}\)\(\vec{b}\)的逆时针方向。
\(=0\)\(\vec{a}\)\(\vec{b}\)共线。

Code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
struct point{double x,y;};
struct segment{point a,b;};
double cross(point a,point b){return a.x*b.y-b.x*a.y;}
point vec(point a,point b){return {b.x-a.x,b.y-a.y};}
bool intersect(segment a,segment b){
	if(max(b.a.x,b.b.x)<min(a.a.x,a.b.x)||min(b.a.x,b.b.x)>max(a.a.x,a.b.x)||
		max(b.a.y,b.b.y)<min(a.a.y,a.b.y)||min(b.a.y,b.b.y)>max(a.a.y,a.b.y)) return 0;
	double t1=cross(vec(b.a,a.a),vec(b.a,a.b))*cross(vec(b.b,a.a),vec(b.b,a.b));
	double t2=cross(vec(a.a,b.a),vec(a.a,b.b))*cross(vec(a.b,b.a),vec(a.b,b.b));
	if(t1>0||t2>0) return 0;
	return 1;
}
int n;
list<pair<int,segment>> li;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	while(cin>>n){
		if(!n) break;
		li.clear();
		for(int i=1;i<=n;i++){
			segment ts;
			cin>>ts.a.x>>ts.a.y>>ts.b.x>>ts.b.y;
			for(auto it=li.begin();it!=li.end();){
				if(intersect(ts,(*it).second)) it=li.erase(it);
				else it++;
			}
			li.push_back({i,ts});
		}
		cout<<"Top sticks: ";
		bool f=0;
		for(auto i:li){
			if(!f) f=1;
			else cout<<", ";
			cout<<i.first;
			if(!f) f=1,cout<<", ";
		}
		cout<<".\n";
	}
	return 0;
}
posted @ 2024-07-16 09:20  Sinktank  阅读(16)  评论(0编辑  收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.