简单的计算几何

1 向量

这里就不介绍向量的加法和减法,着重介绍一下向量的叉积的作用和代码实现
a), 可以判断点在直线的左边还是右边 ad: POJ2318(当时poj挂了进不去)
b), 计算三角形的面积 ( 两个向量的叉积的1/2的绝对值 )
c),在计算凸包中起到了重要作用 ( 下面会讲 )
当然了可能会用到一些重载 详情请参见 kuang_bin.blog

三角形

a). 求三角形的面积
方法一:海伦公式

p = ( a + b + c ) / 2 ; S = p ( p a ) ( p b ) ( p c ) = 1 4 ( a + b + c ) ( a + b c ) ( a + c b ) ( b + c a )

方法二:
S a b c = | 1 2 A B A C |

b). 判断点是否在三角形内
面积法 : S a b c = S p b c + S a p c + S a b p
叉积法:可以利用叉积的正负号判断,这一结论可以扩展到凸多边形

多边形

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公式求多边形内部定点数

S ( ) = a ( ) + 1 2 b ( ) 1

对于一个左闭右开的线段它的点数为 g c d ( a b s ( B y A y ) , a b s ( B x A x ) )

#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;
}

猜你喜欢

转载自blog.csdn.net/Acer12138/article/details/81229762