UVA10902 Pick-up Sticks 题解
Description
按顺序给出 \(n\) 个棍子两个端点的坐标。如果后来的棍子与前边的棍子相交,则说后面的把前面的挡住了。问最后有多少个棍子没被挡住。
\(n\leq 10^5\),且答案不超过 \(1000\)。
Solution
叉积基本运用。
-
定义:\(\overrightarrow{a} \times \overrightarrow{b} = |\overrightarrow{a}||\overrightarrow{b}|\sin \theta\)。
-
几何意义:向量 \(\overrightarrow{a}\) 与向量 \(\overrightarrow{b}\) 张成的平行四边形的有向面积。\(\overrightarrow{b}\) 在 \(\overrightarrow{a}\) 的逆时针方向为正,顺时针方向为负。
-
坐标运算:\(\overrightarrow{a} \times \overrightarrow{b}=x_1y_2 - y_1x_2\)。
-
应用:
(1):判定点线的位置关系
\((\overrightarrow{b}-\overrightarrow{a}) \times (\overrightarrow{c}- \overrightarrow{a}) > 0 \Rightarrow c\) 点在直线 \(ab\) 的左侧;
\((\overrightarrow{b}-\overrightarrow{a}) \times (\overrightarrow{c}- \overrightarrow{a}) < 0 \Rightarrow c\) 点在直线 \(ab\) 的右侧;
$(\overrightarrow{b}-\overrightarrow{a}) \times (\overrightarrow{c}- \overrightarrow{a}) = 0 \Rightarrow $ 三点共线。
il double operator *(Point a,Point b) { return a.x*b.y - a.y*b.x; }//叉积
il double cross(Point a,Point b,Point c) { return (b-a)*(c-a); }//判断c和直线ab的关系
(2):判定线线之间的位置关系
①cross(a,b,c) * cross(a,b,d) > 0
\(\Rightarrow\) 直线 \(ab\) 与 线段 \(cd\) 无交点。
②cross(a,b,c) * cross(a,b,d) <= 0
\(\Rightarrow\) 直线 \(ab\) 与 线段 \(cd\) 有交点。
\(= 0\) 的情况就是端点 \(c\) 或 \(d\) 在直线 \(ab\) 上。
③线段相交
\(\text{Part 1}\):判断必不可交的情况
//如果四条判断有一个为真,则代表两线段必不可交,否则应该进行第二步判断。
max(c.x,d.x)<min(a.x,b.x) || max(c.y,d.y)<min(a.y,b.y) || max(a.x,b.x)<min(c.x,d.x) || max(a.y,b.y)<min(c.y,d.y)
\(\text{Part 2}\): 利用叉积求交
Ⅰ.cross(a,b,c) * cross(a,b,d) > 0
或 cross(c,d,a) * cross(c,d,b) > 0
\(\Rightarrow\) 线段 \(ab\) 与线段 \(cd\) 无交点。
Ⅱ.cross(a,b,c) * cross(a,b,d) <= 0
且 cross(c,d,a) * cross(c,d,b) <= 0
\(\Rightarrow\) 线段 \(ab\) 与线段 \(cd\) 有交点。
注意有交的时候必须两个条件都满足,这样就排除了一个线段在另一个线段的直线上但是不在线段上的情况。如果不理解可以自己推一推。
由于答案不会超过 \(1000\) ,玄学复杂度暴力 \(O(n^2)\) 就能通过。
Code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
//#define int long long
#define ll long long
#define y1 y11
#define re register
#define il inline
const int N = 1e5 + 5;
using namespace std;
struct Point{
double x,y;
}a[N],b[N];
int n,m,ans[N],cnt;
bool flag = 1;
il int read()
{
int f=0,s=0;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f |= (ch=='-');
for(; isdigit(ch);ch=getchar()) s = (s<<1) + (s<<3) + (ch^48);
return f ? -s : s;
}
Point operator +(Point a,Point b) { return Point{a.x+b.x,a.y+b.y}; }//加
Point operator -(Point a,Point b) { return Point{a.x-b.x,a.y-b.y}; }//减
il double operator *(Point a,Point b) { return a.x*b.y - a.y*b.x; }//叉积
il double operator &(Point a,Point b) { return a.x*b.x + a.y*b.y; }//点积
il double cross(Point a,Point b,Point c) { return (b-a)*(c-a); }//判断c和直线ab的关系
il double len(Point a) { return sqrt(a&a); }
il double dis(Point a,Point b) { return len(b-a); }
il bool Intersect(int i,int j)
{
return (cross(a[i],b[i],a[j])*cross(a[i],b[i],b[j]) <= 0) && (cross(a[j],b[j],a[i])*cross(a[j],b[j],b[i]) <= 0);
}
il void Main()
{
n = read();
if(!n) exit(0);
cnt = 0;
for(re int i=1;i<=n;i++) scanf("%lf%lf%lf%lf",&a[i].x,&a[i].y,&b[i].x,&b[i].y);
for(re int i=1;i<=n;i++)
{
flag = 1;
for(re int j=i+1;j<=n;j++)
if(Intersect(i,j)) { flag = 0; break; }
if(flag) ans[++cnt] = i;
}
printf("Top sticks:");
for(re int i=1;i<cnt;i++) printf(" %d,",ans[i]);
printf(" %d.\n",ans[cnt]);
}
signed main()
{
while(1) Main();
return 0;
}