判断点和射线的那边
向量叉乘判断符号解决。向量叉乘使用右手法则。
v
1
=
(
x
1
,
y
1
)
,
v
2
=
(
x
2
,
y
2
)
v
1
×
v
2
=
x
1
∗
y
2
−
y
1
∗
x
2
v_1=(x1,y1),v2=(x2,y2) \\ v1×v2=x1*y2-y1*x2
v1=(x1,y1),v2=(x2,y2)v1×v2=x1∗y2−y1∗x2
例:
HDU 6857 依次给出三个点,求是顺时针旋转还是逆时针。
s
=
v
13
×
v
23
=
(
x
1
−
x
3
)
(
y
2
−
y
3
)
−
(
y
1
−
y
3
)
(
x
2
−
x
3
)
s=v_{13}×v_{23}= (x1-x3)(y2-y3)-(y1-y3)(x2-x3)
s=v13×v23=(x1−x3)(y2−y3)−(y1−y3)(x2−x3)
则结合右手法则可知,
{
s
<
0
,
顺
时
针
s
>
0
,
逆
时
针
\{\begin{aligned}s<0, 顺时针\\ s>0, 逆时针\end{aligned}
{s<0,顺时针s>0,逆时针
POJ 3304
判断n个线段能否投影到一条直线上至少有一个共同点。容易转化为问题:n个线段是否能被一条直线(与投影轴垂直)穿过。枚举所有点两两成的直线能否串过所有线段。用跨立实验判断直线和线段是否相交。
#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<utility>
#include<cmath>
#include<stack>
#include<set>
#include<cstring>
#include<string>
using namespace std;
// #define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=1e5+3;
const ll mod = 1e9+7;
struct Point{
double x,y;
Point operator- (const Point &v) const {
return Point{x-v.x, y-v.y};
}
int operator* (const Point &v) const {
double flag = x*v.y - y*v.x;
if(abs(flag)<1e-8)return 0;
return flag>0 ? 1 : -1;
}
double len(){
return sqrt(x*x+y*y);
}
};
struct Line{
Point p1, p2;
int judge_point(Point v){
Point pa = p1 - p2;
Point pb = p1 - v;
return pa * pb;
}
};
Point p[305];
Line seg[305];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int tt;
cin>>tt;
while(tt--){
int n;
cin>>n;
for(int i=0;i<2*n;++i){
cin>>p[i].x>>p[i].y;
}
for(int i=0;i<n;++i){
seg[i] = Line{p[2*i],p[2*i+1]};
}
int flag = false;
for(int i=0;i<2*n && !flag;++i){
for(int j=i+1;j<2*n && !flag;++j){
if((p[i]-p[j]).len()<1e-8)continue; // key!!! 两个点相同就不能成直线
Line lne = Line{p[i],p[j]};
bool f=true;
for(int k=0;k<n;++k){
int f1 = lne.judge_point(seg[k].p1);
int f2 = lne.judge_point(seg[k].p2);
if(!f1 || !f2 || f1!=f2)continue;
f=false;
break;
}
if(f)flag = true;
}
}
if(flag)cout<<"Yes!"<<endl;
else cout<<"No!"<<endl;
}
}
Intersecting Lines
求两直线交点
x0 = ((x3-x4) * (x2*y1 - x1*y2) - (x1-x2) * (x4*y3 - x3*y4)) / ((x3-x4) * (y1-y2) - (x1-x2) * (y3-y4));
y0 = ((y3-y4) * (y2*x1 - y1*x2) - (y1-y2) * (y4*x3 - y3*x4)) / ((y3-y4) * (x1-x2) - (y1-y2) * (x3-x4));
#include<iostream>
#include<vector>
#include<algorithm>
#include <iomanip>
#include<map>
#include<utility>
#include<cmath>
#include<stack>
#include<set>
#include<cstring>
#include<string>
using namespace std;
// #define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=1e5+3;
const ll mod = 1e9+7;
struct Point{
double x,y;
Point operator- (const Point &v) const {
return Point{x-v.x, y-v.y};
}
int operator* (const Point &v) const {
double flag = x*v.y - y*v.x;
if(abs(flag)<1e-8)return 0;
return flag>0 ? 1 : -1;
}
double len(){
return sqrt(x*x+y*y);
}
};
struct Line{
Point p1, p2;
int judge_point(Point v){
Point pa = p1 - p2;
Point pb = p1 - v;
return pa * pb;
}
};
Point p[305];
Line seg[305];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int tt;
cin>>tt;
cout<<"INTERSECTING LINES OUTPUT"<<endl;
while(tt--){
Line la, lb;
cin>>la.p1.x>>la.p1.y>>la.p2.x>>la.p2.y>>lb.p1.x>>lb.p1.y>>lb.p2.x>>lb.p2.y;
Point va = la.p1 - la.p2;
Point vb = lb.p1 - lb.p2;
int flag = va * vb;
if(!flag){
if(!la.judge_point(lb.p1))cout<<"LINE"<<endl;
else cout<<"NONE"<<endl;
continue;
}
double x1 = la.p1.x;
double y1 = la.p1.y;
double x2 = la.p2.x;
double y2 = la.p2.y;
double x3 = lb.p1.x;
double y3 = lb.p1.y;
double x4 = lb.p2.x;
double y4 = lb.p2.y;
double x0 = ((x3-x4) * (x2*y1 - x1*y2) - (x1-x2) * (x4*y3 - x3*y4)) / ((x3-x4) * (y1-y2) - (x1-x2) * (y3-y4));
double y0 = ((y3-y4) * (y2*x1 - y1*x2) - (y1-y2) * (y4*x3 - y3*x4)) / ((y3-y4) * (x1-x2) - (y1-y2) * (x3-x4));
cout<<"POINT "<<fixed<<setprecision(2)<<x0<<" "<<y0<<endl;
}
cout<<"END OF OUTPUT"<<endl;
}
POJ - 1556
每个点两两看有没有穿过墙,没穿过则建边,跑最短路。
没有做快速排斥实验,因为不可能有边与墙共线的情况。
#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<utility>
#include<cmath>
#include<stack>
#include<set>
#include<cstring>
#include<string>
#include<iomanip>
#include<queue>
// #define endl '\n'
using namespace std;
#define debug(_x) cout<<#_x<<": "<<_x<<endl;
typedef long long ll;
const int maxn = 1e5+4;
struct Point{
double x,y;
double operator^ (const Point &v) const {
double res = x * v.y - v.x * y;
if(abs(res) < 1e-6) return 0;
return res;
}
Point operator- (const Point &v) const {
return Point{x-v.x, y-v.y};
}
double len(){
return sqrt(x*x+y*y);
}
};
struct Line{
Point p1,p2;
Point dir(){
return p1 - p2;
}
};
bool is_cross(Line l1, Line l2){
Point d1 = l1.dir(), d2 = l2.dir();
return ((l1.p1 - l2.p1) ^ d1) * (d1 ^ (l1.p1 - l2.p2)) > 0 && ((l2.p1 - l1.p1) ^ d2) * (d2 ^ (l2.p1 - l1.p2)) > 0;
}
struct Edge{
int to;
double dis;
bool operator< (const Edge& v){
return dis>v.dis;
}
};
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n;
while(cin>>n && ~n){
vector<Point> p;
vector<Line> e;
p.push_back(Point{0,5});
for(int k=0;k<n;++k){
double tx,y[4];
cin>>tx;
for(int i=0;i<4;++i){
cin>>y[i];
p.push_back(Point{tx,y[i]});
}
e.push_back(Line{Point{tx,0},Point{tx,y[0]}});
e.push_back(Line{Point{tx,y[1]},Point{tx,y[2]}});
e.push_back(Line{Point{tx,y[3]},Point{tx,10}});
}
p.push_back(Point{10,5});
vector<vector<Edge> > G(p.size());
vector<bool> in(p.size());
vector<double> d(p.size(), 1e8);
for(int i=0;i<p.size();++i){
for(int j=i+1;j<p.size();++j){
if(p[i].x == p[j].x)continue;
int flag = 1;
Line lp = Line{p[i],p[j]};
for(int k=0;k<e.size();++k){
if(is_cross(e[k],lp)){
flag = 0;
break;
}
}
if(flag){
G[i].push_back(Edge{j, (p[j]-p[i]).len()});
}
}
}
queue<Edge> Q;
d[0] = 0;
Q.push(Edge{0,0});
while(!Q.empty()){
Edge u = Q.front();
in[u.to] = 0;
Q.pop();
for(int i=0;i<G[u.to].size();++i){
Edge v = G[u.to][i];
if(d[v.to]>d[u.to]+v.dis){
d[v.to] = d[u.to] + v.dis;
if(in[v.to])continue;
in[v.to] = 1;
v.dis = d[v.to];
Q.push(v);
}
}
}
cout<<fixed<<setprecision(2)<<d[p.size()-1]<<endl;
}
}
POJ - 2653
木棒压木棒,求没被压的木棒。
#include<iostream>
#include<vector>
#include<algorithm>
#include <iomanip>
#include<map>
#include<utility>
#include<cmath>
#include<stack>
#include<set>
#include<cstring>
#include<string>
#include<list>
#include<cstdio>
using namespace std;
// #define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
struct Point{
double x, y;
double operator* (const Point &p) const {
return x*p.y - p.x*y;
}
Point operator- (const Point &p) const {
return Point{x-p.x, y-p.y};
}
double len(){
return sqrt(x*x+y*y);
}
};
struct Line{
Point a,b;
Point dir() const{
return a - b;
}
double judge_point(const Point &p) const{
return this->dir() * (a - p);
}
};
bool iscross(Line s1,Line s2){
return min(s1.a.x,s1.b.x) < max(s2.a.x,s2.b.x)
&& min(s1.a.y,s1.b.y) < max(s2.a.y,s2.b.y)
&& min(s2.a.x,s2.b.x) < max(s1.a.x,s1.b.x)
&& min(s2.a.y,s2.b.y) < max(s1.a.y,s1.b.y)
&& s1.judge_point(s2.a) * s1.judge_point(s2.b) <= 0
&& s2.judge_point(s1.a) * s2.judge_point(s1.b) <= 0;
}
int main(){
int n;
while(~scanf("%d", &n) && n){
list<pair<Line,int> > lines;
Line tl;
for(int i = 0;i < n;++i){
scanf("%lf%lf%lf%lf",&tl.a.x,&tl.a.y,&tl.b.x,&tl.b.y);
for(list<pair<Line,int> >::iterator it = lines.begin();it!=lines.end();){
if(iscross(it->first, tl))it = lines.erase(it);
else ++it;
}
lines.push_back(make_pair(tl, i+1));
}
printf("Top sticks: %d", lines.begin()->second);
for(list<pair<Line,int> >::iterator it = lines.begin();it!=lines.end();++it){
if(it == lines.begin())continue;
printf(", %d", it->second);
}
printf(".\n");
}
}
POJ - 1066
每次从线段的中点穿过,问穿过多少面墙能到达宝藏位置。由于每条直线穿过整个正方形。转弯无法减少穿过数。所以枚举每个中点作射线就行。而枚举每个顶点也一样。
#include<iostream>
#include<vector>
#include<algorithm>
#include <iomanip>
#include<map>
#include<utility>
#include<cmath>
#include<stack>
#include<set>
#include<cstring>
#include<string>
#include<list>
#include<cstdio>
using namespace std;
// #define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
struct Point{
double x, y;
double operator* (const Point &p) const {
return x*p.y - p.x*y;
}
Point operator- (const Point &p) const {
return Point{x-p.x, y-p.y};
}
double len(){
return sqrt(x*x+y*y);
}
bool operator== (const Point &p) const {
return x==p.x && y==p.y;
}
};
struct Line{
Point a,b;
Point dir() const{
return a - b;
}
double judge_point(const Point &p) const{
return this->dir() * (a - p);
}
};
bool iscross(Line s1,Line s2){
return min(s1.a.x,s1.b.x) <= max(s2.a.x,s2.b.x)
&& min(s1.a.y,s1.b.y) <= max(s2.a.y,s2.b.y)
&& min(s2.a.x,s2.b.x) <= max(s1.a.x,s1.b.x)
&& min(s2.a.y,s2.b.y) <= max(s1.a.y,s1.b.y)
&& s1.judge_point(s2.a) * s1.judge_point(s2.b) < 1e-8
&& s2.judge_point(s1.a) * s2.judge_point(s1.b) < 1e-8;
}
int main(){
int n;
while(~scanf("%d", &n)){
vector<Line> lines;
vector<Point> p;
Line tl;
for(int i = 0;i < n;++i){
scanf("%lf%lf%lf%lf",&tl.a.x,&tl.a.y,&tl.b.x,&tl.b.y);
lines.push_back(tl);
p.push_back(tl.a);
p.push_back(tl.b);
}
int ans = 1e9;
scanf("%lf%lf",&tl.b.x,&tl.b.y);
for(int k=0;k<p.size();++k){
int no=0;
tl.a = p[k];
for(int i=0;i<lines.size();++i){
no+=iscross(tl, lines[i]);
}
ans = min(ans,no);
}
if(ans > 2*n)ans = 1;
printf("Number of doors = %d\n", ans);
}
}
POJ - 1696
从Plant A出发,只能往左转最多能走多少个点,依次输出。只要每次选当前点为基准,极角排序,选极角最小的,必能走完所有的点。
#include<iostream>
#include<vector>
#include<algorithm>
#include <iomanip>
#include<map>
#include<utility>
#include<cmath>
#include<stack>
#include<set>
#include<cstring>
#include<string>
#include<list>
#include<cstdio>
#define endl '\n'
using namespace std;
struct Point{
int idx;
int x, y;
int operator^ (const Point &p) const{
return x*p.y - y*p.x;
}
Point operator- (const Point &p) const{
return Point{0, x-p.x, y-p.y};
}
double len(){
return sqrt(x*x+y*y);
}
};
int sgn(const int x){
if(abs(x)<1e-8)return 0;
return x < 0 ? -1 : 1;
}
Point mark;
bool cmp(const Point &a, const Point &b){
Point da = a - mark;
Point db = b - mark;
switch(sgn(da ^ db)){
case 1:
return 1;
case -1:
return 0;
default:
return da.len() < db.len();
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int tt;
cin>>tt;
while(tt--){
int n;
cin>>n;
vector<Point> v(n);
for(int i=0;i<n;++i){
cin>>v[i].idx>>v[i].x>>v[i].y;
if(!i)mark = v[i];
else if(v[i].y < mark.y)mark = v[i];
}
swap(v[mark.idx-1],v[0]);
for(int i=1;i<n;++i){
mark = v[i-1];
sort(v.begin()+i,v.end(),cmp);
}
cout<<n;
for(int q = 0; q < n; ++q){
cout<<' '<<v[q].idx;
}
cout<<endl;
}
}
Dancing Stars on Me
给出N个乱序的点,问这些点能不能组成正N边形。
思路:先找到y坐标最小的那个点,作为第一个点,将后面N-1个点依次以上一个点为KEY点做极角排序。判断每条边长度是否相同,相邻边的角度是否相同(可用内积,外积)。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
const int inf = 2e9;
const double eps = 1e-6;
using ll = long long;
struct Point{
int x, y;
double len(){
return sqrt(1ll*x*x+1ll*y*y);
}
Point operator- (const Point& ot) const{
return Point{x-ot.x, y-ot.y};
}
ll operator* (const Point& ot) const{
return x*ot.y - y*ot.x;
}
}key;
int sgn(double x){
if(x==0)return 0;
return x < 0 ? -1 : 1;
}
bool cmp(const Point& a, const Point& b){
Point va = a - key, vb = b - key;
switch(sgn(va * vb)){
case 0:
return va.len() < vb.len();
case 1:
return 1;
case -1:
return 0;
}
}
int main(){
int tt;
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> tt;
while(tt--){
int n;
cin >> n;
vector<Point> v;
int idx = 0;
for(int i=0;i<n;++i){
Point x;
cin >> x.x >> x.y;
v.push_back(x);
if(x.y < v[0].y || x.y==v[0].y && x.x < v[0].x)
swap(v[i], v[0]);
}
for(int i=0;i+1<n;++i){
key = v[i];
sort(v.begin()+i+1, v.end(), cmp);
}
v.push_back(v[0]);
v.push_back(v[1]);
int flag = 1;
ll inj = -1;
for(int i=0;i<n;++i){
// cout << v[i].x << "," << v[i].y << endl;
Point a = v[i+1] - v[i], b = v[i+2] - v[i+1];
if(i==0)inj = a * b;
if(a.len()!=b.len() || a*b != inj){
flag = 0;
break;
}
}
cout << (flag ? "YES" : "NO") << endl;
}
}
Surround the Trees
求二维凸包的周长。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
const int inf = 2e9;
const double eps = 1e-6;
using ll = long long;
using pdd = pair<double, double>;
const double PI = acos(-1);
const int maxn = 2e5+5;
int sgn(double x){
if(x==0)return 0;
return x < 0 ? -1 : 1;
}
struct Point{
int x, y;
double len(){
return sqrt(1ll*x*x+1ll*y*y);
}
Point operator- (const Point& ot) const{
return Point{x-ot.x, y-ot.y};
}
ll operator* (const Point& ot) const{
return x*ot.y - y*ot.x;
}
}key, sta[maxn];
bool cmp(const Point& a, const Point& b){
Point va = a - key, vb = b - key;
switch(sgn(va * vb)){
case 0:
return va.len() < vb.len();
case 1:
return 1;
case -1:
return 0;
}
}
int main(){
int tt;
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n;
while(cin >> n && n){
vector<Point> v(n);
for(Point& p : v){
cin >> p.x >> p.y;
}
if(n==1){
cout << 0 << endl;
continue;
}else if(n==2){
cout << fixed << setprecision(2) << (v[1] - v[0]).len() << endl;
continue;
}
int id = 0;
for(int i=1;i<n;++i){ // 选出最下,最左的点
if(v[i].y < v[id].y || v[i].y == v[id].y && v[i].x < v[id].x)id = i;
}
swap(v[0], v[id]);
key = v[0];
sort(v.begin()+1, v.end(), cmp); // 极角排序
int top = 0;
sta[0] = v[0];
for(int i=1;i<n;++i){
while(top > 0 && sgn( (sta[top] - sta[top-1]) * (v[i] - sta[top]) ) < 0)
top--; // 踢掉右转的
sta[++top] = v[i];
}
double sum = 0;
sta[++top] = v[0];
for(int i=0;i<top;++i){
sum += (sta[i+1] - sta[i]).len();
}
cout << fixed << setprecision(2) << sum << endl;
}
}
旋转卡壳
[HNOI2007]最小矩形覆盖
题意:二维平面给一堆点,求最小的矩形包括所有的点。输出矩形面积和4个坐标。
思路:旋转卡壳模板。维护三个卡壳点,因为三者都是单峰函数,所以可以实现。
- p是离当前枚举边 ( i , i + 1 ) (i, i + 1) (i,i+1)最远的点,用叉乘求三角形面积来判断,通过p就求出了矩形的高H
- fr是所有点投影到当前枚举边 ( i , i + 1 ) (i, i + 1) (i,i+1),设 i i i到 i + 1 i+1 i+1为正方向,最负的点
- ba是最正的点,fr、ba使用点乘求投影来判断
#include<bits/stdc++.h>
// #define endl '\n'
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
using namespace std;
const int N = 5e4 +5;
const double eps = 1e-10;
struct Coor{
double x, y;
double operator^ (const Coor & ot) const &{
return x * ot.y - y * ot.x;
}
double operator* (const Coor & ot) const &{
return x * ot.x + y * ot.y;
}
double len(){
return sqrt(x * x + y * y);
}
Coor operator- (const Coor & ot) const &{
return Coor{x - ot.x, y - ot.y};
}
Coor operator+ (const Coor & ot) const &{
return Coor{x + ot.x, y + ot.y};
}
}a[N], sta[N], key, rect[4];
Coor operator* (const double & c, const Coor & v){
return Coor{v.x * c, v.y * c};
}
int sgn(double v){
if(abs(v) < eps)return 0;
return v < 0 ? -1 : 1;
}
bool cmp(const Coor &a, const Coor &b){
Coor da = a - key, db = b - key;
switch(sgn(da ^ db)){
case 1:
return true;
case -1:
return false;
default:
return da.len() < db.len();
}
}
double area(int i, int j, int k){
return (sta[j] - sta[i]) ^ (sta[k] - sta[i]);
}
double project(int i, int j, int k){
return (sta[j] - sta[i]) * (sta[k] - sta[i]);
}
int main(){
IOS;
int n;
cin >> n;
int id = 0;
for(int i = 0; i < n; ++i){
cin >> a[i].x >> a[i].y;
if(a[i].y < a[id].y || a[i].y == a[id].y && a[i].x < a[id].x)id = i;
}
swap(a[id], a[0]);
key = a[0];
sort(a+1, a+n, cmp);
int top = 0;
sta[0] = a[0];
#define U(_ar, _i) cout << "(" << _ar[_i].x << "," << _ar[_i].y << ") ";
for(int i = 1; i < n; ++i){
while(top > 0 && sgn((sta[top] - sta[top - 1]) ^ (a[i] - sta[top])) <= 0)top--;
sta[++top] = a[i];
}
sta[++top] = sta[0];
int p = 1, ba = 1, fr = 1;
double ans = 2e14;
for(int i = 0; i < top; ++i){
while(sgn(area(i, i + 1, p) - area(i, i + 1, p + 1)) <= 0)
p = (p + 1) % top;
while(sgn(project(i, i + 1, fr)) > 0 || sgn(project(i, i + 1, fr) - project(i, i + 1, (fr + 1) % top)) >= 0)
fr = (fr + 1) % top;
while(sgn(project(i, i + 1, ba) - project(i, i + 1, (ba + 1) % top)) < 0)
ba = (ba + 1) % top;
double L = (sta[i + 1] - sta[i]).len(), H = area(i, i + 1, p), C = (sta[i + 1] - sta[i]) * (sta[ba] - sta[fr]);
// assert(sgn(C) > 0);
// assert(sgn(H) > 0);
if(sgn(H * C - ans * L * L) < 0){
ans = H * C / (L * L);
H /= L;
Coor I = 1 / L * (sta[i + 1] - sta[i]); // 单位向量
double pf = (sta[fr] - sta[i]) * I; // f投影大小
double pb = (sta[ba] - sta[i]) * I; // b投影大小
rect[0] = sta[i] + pf * I;
rect[1] = sta[i] + pb * I;
Coor Vert = sgn(I.y) == 0 ? Coor{0, 1} : Coor{1, -I.x / I.y};
if(sgn(I ^ Vert) < 0)Vert = -1 * Vert;
Vert = H / Vert.len() * Vert;
rect[2] = rect[1] + Vert;
rect[3] = rect[0] + Vert;
// cout << ans << endl;
// for(int fi = 0, i = 0; i < 4; ++i){
// cout << rect[fi].x << " " << rect[fi].y << endl;
// fi = (fi + 1) % 4;
// }
}
}
if(sgn(ans) == 0) ans = 0;
cout << fixed << setprecision(5) << ans + 1e-8 << endl;
int fi = 0;
for(int i = 1; i < 4; ++i){
if(sgn(rect[i].y - rect[fi].y) < 0 || sgn(rect[i].y - rect[fi].y) == 0 && sgn(rect[i].x - rect[fi].x) < 0)
fi = i;
}
for(int i = 0; i < 4; ++i){
if(sgn(rect[fi].x) == 0)rect[fi].x = 0;
if(sgn(rect[fi].y) == 0)rect[fi].y = 0;
cout << rect[fi].x << " " << rect[fi].y << endl;
fi = (fi + 1) % 4;
}
}