dzy哥哥回来给我们上课
杜老师好帅啊qwq
重新写计算几何
这些知识你需要自己推一遍
- 点
- 线段,及点在线段上的表示(两种向量法-一个叉乘一个等和线,一种距离法)
- 直线及其表示,用点斜法存储(学习斜率优化)
struct node3{
double x,y,k;
}line[NNN];
- 直线引申为半平面
- 角度
double angle[NNN];//角度表示,用弧度制,省略单位pi
- 多边形:按顺序(顺/逆)存点,有时候也会用有向线段(或一个表示位置的点和其他边)
- 圆:存圆心和半径
- 两点之间的距离
- 面积:用叉乘就很好做
- 小心点到直线的距离和点到线段的距离有区别
- 判断线段是否相交,用看旋转,判两次
- 求比例用相似导一下就行了
- 作垂线:平行+旋转
- 作角平分线:用对称性
- 判断点是否在多边形内部:连的射线与边的交点奇偶(奇在内,偶在外),注意特判一些情况
- 与圆相关的点:用交点推一下就行了
练习
POJ1269
特判就特判,不是特殊情况再求交点
POJ3907
第一道计算几何提:D
code
#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define in Read()
#define re register
const int NNN=1e6+10;
int n;
struct node{
double x,y;
friend inline double operator * (const node &a,const node &b){
return a.x*b.y-a.y*b.x;
}
}v[NNN];
double ans;
int main(){
while(scanf("%d",&n)!=EOF&&n){
for(re int i=1;i<=n;i++)scanf("%lf%lf",&v[i].x,&v[i].y);
ans=0;
for(re int i=1;i<=n;i++)ans+=v[i]*v[i%n+1];
printf("%d\n",(int)(fabs(ans)/2));
}
return 0;
}
向量的运算
以前的代码好丑啊,但是我懒得改了
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
#define cross xor
#define point or
const int NNN=1e4+10;
struct node1{
double x,y;
}dot[NNN];
struct node2{
double x,y;
friend inline double operator cross (const node2 &u,const node2 &v){//叉乘
return 1.0*u.x*v.y-1.0*u.y*v.x;
}
friend inline double operator point (const node2 &u,const node2 &v){//点乘
return 1.0*u.x*v.x+1.0*u.y*v.y;
}
friend inline node2 operator + (const node2 &u,const node2 &v){//向量加法
node2 ans;
ans.x=u.x+v.x;
ans.y=u.y+v.y;
return ans;
}
friend inline node2 operator - (const node2 &u,const node2 &v){//向量减法
node2 ans;
ans.x=u.x-v.x;
ans.y=u.y-v.y;
return ans;
}
}vec[NNN];
inline node2 operator - (const node1 &a,const node1 &b){//a到b的向量(a减b)注意顺序
node2 ans;
ans.x=a.x-b.x;
ans.y=a.y-b.y;
return ans;
}
inline node1 operator + (const node1 &a,const node2 &v){//已知起点和向量求终点
node1 ans;
ans.x=a.x+v.x;
ans.y=a.y+v.y;
return ans;
}
int main(){
return 0;
}
其他运算:
- 向量的夹角:
c o s < a ⃗ , b ⃗ > = a ⃗ ⋅ b ⃗ ∣ a ⃗ ∣ ∣ b ⃗ ∣ cos<\vec{a},\vec{b}>=\frac{\vec{a}·\vec{b}}{|\vec{a}||\vec{b}|} cos<a,b>=∣a∣∣b∣a⋅b
s i n < a ⃗ , b ⃗ > = ∣ a ⃗ × b ⃗ ∣ ∣ a ⃗ ∣ ∣ b ⃗ ∣ sin<\vec{a},\vec{b}>=\frac{|\vec{a}\times\vec{b}|}{|\vec{a}||\vec{b}|} sin<a,b>=∣a∣∣b∣∣a×b∣
- 正余弦定理:自己推
- 向量的旋转:用复平面推(用普通的旋转推也行,就是要注意一下正负)
- 叉乘运用到的行列式:
行列式的运算,每行任取一个不在同一列的数,相乘
再依次排列他们的列数,其中的逆序对个数mod2就得到该积的符号(0为正,1为负) - 叉乘运用到右手法则
- 向量的拐向用叉乘
- 夹角的优劣用叉乘,夹角的钝锐用点乘(这里用到的思想就是必修四“看角的教材”里很好的思想了)
凸包
每个点(向量)要通过极角排一下序
POJ3348板子题
code
#include<bits/stdc++.h>
#define in Read()
#define re register
#define swap std::swap
#define sort std::sort
inline int in{
int i=0,f=1;char ch;
while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch<='9'&&ch>='0')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=2000+10;
const double eps=1e-6;
int num;
struct node{
double x,y;
node(){}
node(double _x,double _y){x=_x,y=_y;}
friend node operator - (const node u,const node v){return node(u.x-v.x,u.y-v.y);}
friend node operator + (const node u,const node v){return node(u.x+v.x,u.y+v.y);}
friend node operator * (const node u,const double a){return node(u.x*a,u.y*a);}
friend node operator / (const node u,const double a){return node(u.x/a,u.y/a);}
friend double operator * (const node u,const node v){return (u.x*v.x+u.y*v.y);}
friend double operator ^ (const node u,const node v){return (u.x*v.y-u.y*v.x);}
void PRINT_VEC(){printf("%.2lf %.2lf",x,y);}
void SCAN_VEC(){scanf("%lf%lf",&x,&y);}
}vec[NNN],convec[NNN],O;
inline double dist(node u){return sqrt(u*u);}
inline double cros(node u,node v,node w){return (u-v)^(w-v);}//u←v→w
inline bool operator < (const node u,const node v){
double res=cros(u,vec[1],v);
return res==0?u.x<v.x:res>0;
}
int main(){
O.x=0,O.y=0;
int n=in,id=1;
for(re int i=1;i<=n;++i){
vec[i].SCAN_VEC();
if(vec[i].x<vec[id].x||(fabs(vec[i].x-vec[id].x)<eps&&vec[i].y<vec[id].y))id=i;
}
swap(vec[1],vec[id]);
sort(vec+2,vec+n+1);
// for(re int i=1;i<=n;++i)vec[i].PRINT_VEC(),puts("");
convec[++num]=vec[1];
for(re int i=2;i<=n;++i){
while(num>1&&cros(convec[num-1],convec[num],vec[i])>0)--num;
convec[++num]=vec[i];
}
// for(re int i=1;i<=num;++i)convec[i].PRINT_VEC(),puts("");
convec[0]=convec[num];
double res=0;
for(re int i=1;i<=num;++i)res+=cros(convec[i-1],O,convec[i]);
printf("%d",(int)res/100);
return 0;
}
旋转卡壳
看似O(N2)实际O(N)
是不是很妙?
很久以后再看代码,发现这真是一个旋转。。。
这个代码RE了,烦请各位大佬指正
//RE码,懒得改了
#include<cmath>
#include<cstdio>
#include<algorithm>
#define in Read()
#define re register
#define swap std::swap
#define sort std::sort
#define max std::max
inline int in{
int i=0,f=1;char ch;
while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch<='9'&&ch>='0')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
int n,id;
const int NNN=5000+10;
const double eps=1e-6;
int num;
struct node{
double x,y;
node(){}
node(double _x,double _y){x=_x,y=_y;}
friend node operator - (const node u,const node v){return node(u.x-v.x,u.y-v.y);}
friend node operator + (const node u,const node v){return node(u.x+v.x,u.y+v.y);}
friend node operator * (const node u,const double a){return node(u.x*a,u.y*a);}
friend node operator / (const node u,const double a){return node(u.x/a,u.y/a);}
friend double operator * (const node u,const node v){return (u.x*v.x+u.y*v.y);}
friend double operator ^ (const node u,const node v){return (u.x*v.y-u.y*v.x);}
void PRINT_VEC(){printf("%.2lf %.2lf",x,y);}
void SCAN_VEC(){scanf("%lf%lf",&x,&y);}
}vec[NNN],convex[NNN],O;
inline double dist(node u){return sqrt(u*u);}
inline double cros(node u,node v,node w){return (u-v)^(w-v);}//u←v→w
inline bool operator < (const node u,const node v){
double res=cros(u,vec[1],v);
return res==0?u.x<v.x:res>0;
}
inline int nxt(int x){return x==n?1:x+1;}
inline int SOLVE(){
int res=0;
convex[n+1]=convex[1];
for(re int i=1,j=3;i<=n;++i){
while(nxt(j)!=i&&
cros(convex[i],convex[i+1],convex[j])<=cros(convex[i],convex[i+1],convex[j+1]))j=nxt(j);
res=max(res,(int)((convex[j]-convex[i])*(convex[j]-convex[i])));
res=max(res,(int)((convex[j]-convex[i+1])*(convex[j]-convex[i+1])));
}
return res;
}
int main(){
O.x=0,O.y=0;
n=in,id=1;
for(re int i=1;i<=n;++i){
vec[i].SCAN_VEC();
if(vec[i].x<vec[id].x||(fabs(vec[i].x-vec[id].x)<eps&&vec[i].y<vec[id].y))id=i;
}
swap(vec[1],vec[id]);
sort(vec+2,vec+n+1);
convex[++num]=vec[1];
for(re int i=2;i<=n;++i){
while(num>1&&cros(convex[num-1],convex[num],vec[i])>0)--num;
convex[++num]=vec[i];
}
if(num==2){
printf("%d",(int)((convex[1]-convex[2])*(convex[1]-convex[2])));
return 0;
}
int ans=SOLVE();
printf("%d",ans);
return 0;
}
半平面交
处理面积交的问题
与线性规划很相似
polygon内部的所有点构成的点集叫做polygon的核
所有核的交集就是我们要求的面积
算法步骤:
- 极角排序,用atan2
注意,如果你用的是逆时针围成多边形,下面要取的是向量的左侧 - 枚边
能改变半平面交的就入栈
让先前的栈内边无效的,把以前的边弹出去