简单的计算几何
1 向量
这里就不介绍向量的加法和减法,着重介绍一下向量的叉积的作用和代码实现
a), 可以判断点在直线的左边还是右边 ad: POJ2318(当时poj挂了进不去)
b), 计算三角形的面积 ( 两个向量的叉积的1/2的绝对值 )
c),在计算凸包中起到了重要作用 ( 下面会讲 )
当然了可能会用到一些重载 详情请参见 kuang_bin.blog
三角形
a). 求三角形的面积
方法一:海伦公式
方法二:
b). 判断点是否在三角形内
面积法 : S△abc=S△pbc+S△apc+S△abpS△abc=S△pbc+S△apc+S△abp
叉积法:可以利用叉积的正负号判断,这一结论可以扩展到凸多边形
多边形
a) . 凸多边形面积的求法:把凸多边形分成n-2个小三角形然后求面积 ( 对非凸多边形仍然有效 )
代码实现:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn = 1e5+5;
//已知所有的点
struct node{
int x,y;
}ss[maxn];
//利用叉积求面积
double cross(node a,node b,node c){
return (c.x-a.x)*(b.y-a.y)-(b.x-c.x)*(c.y-a.y);
}
double Areas(node *s,int n){
double area = 0;
for (int i = 1;i<n-1;i++) {
area += cross(s[0],s[i],s[i+1]);
}
return fabs(area / 2.0);//因为最后可能方向反了所以求一下绝对值
}
int main(){
int n; cin>>n;
for (int i = 0;i<n;i++) scanf("%d %d",&ss[i].x,&ss[i].y);
double areas = Areas(ss,n);
printf("%.lf\n",areas);
return 0;
}
b).pick公式求多边形内部定点数
对于一个左闭右开的线段它的点数为gcd(abs(By−Ay),abs(Bx−Ax))gcd(abs(By−Ay),abs(Bx−Ax))
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
//现在已知一个多边形所有的点
struct node{
int x,y;
}ss[maxn];
double cross(node a,node b,node c){
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
double Areas(node *s,int n){
double area = 0;
for (int i = 1;i<n-1;i++){
area += cross(s[0],s[i],s[i+1]);
}
return fabs(area / 2.0); //因为可能总的方向反了,所以求一下绝对值
}
int gcd(int a,int b){
if(b == 0){
return a;
} else {
return gcd(b,a%b);
}
}
int PGIV(node *s,int n){ //pick_get_inner_vertix
int num = 0;
double areas = Areas(s,n);
int edNode = 0;
for (int i = 1;i<n;i++) {
edNode += gcd(abs(ss[i].y-ss[i-1].y),abs(ss[i].x-ss[i-1].x));
}
edNode += gcd(abs(ss[0].y-ss[n-1].y),abs(ss[0].x-ss[n-1].x));
int inNode = (2*(areas+1)-edNode) / 2;
return inNode;
}
int main(){
int n; cin>>n;
for (int i = 0;i<n;i++) scanf("%d %d",&ss[i].x,&ss[i].y);
printf("%d\n",PGIV(ss,n));
return 0;
}
凸包
凸包就是把给定点包围在内部的、面积最小的凸多边形。
Andrew算法:
首先把所有点按照从小到大排序(如果x相同,按照y从小到大排
序),删除重复点后得到序列p1; p2……,然后把p1和p2放到凸包
中。从p3开始,当新点在凸包“前进”方向的左边时继续,否则
依次删除最近加入凸包的点,直到新点在左边。
从左到右和从右到左各扫描一次
计算凸包,输入点数组p,个数n,输出点数组ch。函数返回凸包顶点数
输入不能有重复点。函数执行完之后输入点被破坏
如果不希望在凸包的边上有输入点,把两个<=改成<
在精度要求高时建议用用自定义函数dcmp
代码实现:
struct node
{
int x,y;
}ss[maxn],ans[maxn];
int n,m;
bool cmp(node a,node b)
{
return (a.x<b.x)||(a.x==b.x&&a.y<b.y);
}
int cross(node a,node b,node c)
{
return (b.x-a.x)*(c.y-b.y)-(b.y-a.y)*(c.x-b.x);
}
int Andrew()
{
int len,top = 2;
sort(ss,ss+n,cmp);
ans[0] = ss[0],ans[1] = ss[1];
for(int i=2;i<n;i++)
{
while( top>1 && cross(ans[top-1],ans[top-2],ss[i])<=0) top--;
ans[top++]=ss[i];
}
len = top;
for(int i=n-2;i>=0;i--)
{
while( top > len && cross(ans[top-1],ans[top-2],ss[i])<=0) top--;
ans[top++] = ss[i];
}
return top;
}
易错点
几何问题通常的WA点就是精度问题 可以自己写一个eps
const double eps =1e-10;
int dcmp ( double x){
if( fabs (x)<eps ) return 0;
else return x <0? -1:1;
}