[题解]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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效