暑假集训------简单几何计算——————关于向量和凸包的计算

这篇博客介绍了几何计算中的向量操作,如点与直线的关系、线段之间的关系以及点到线段的最短距离计算。讲解了如何判断点是否在三角形内、求多边形面积以及凸包的概念和求解方法。同时提供了相关算法的代码实现,并通过实际题目解析加深理解。
摘要由CSDN通过智能技术生成

2018/7/26  22:33

今天讲的是关于几何计算中有关于向量的一些操作和关于图行的一些简单处理

首先就是关于对向量的操作,一般是用结构体储存x,y,这样方便在结构体中重载操作符,使得可以十分方便的进行数学中的一些向量操作,下面就附上关于"+,-乘"

struct Pt
{
	int x, y;
	Pt (){}//先声明函数,也是构造函数,没有这一步,一下的操作会报错;
	Pt(int _x, int _y)
	{
		x = _x;//这一步的操作是声明你参数里面的x就是结构体的x你的那个y也是结构体的y;
		y = _y;
	}
	Pt operator - ( Pt b) {//这里的x就是‘-’号前面的那个结构体,下同;
		return Pt(x - b.x, y - b.y);
	}
	Pt operator + ( Pt b) {
		return Pt(x + b.x, y + b.y);
	}
	Pt operator * (double a)
	{
		return	Pt(x*a, y*a);
	}
};

下面是向量的点积和矢积:

double dot(Pt a, Pt b)
{
	return	a.x*b.x + a.y*b.y;
}

double det(Pt a, Pt b)//别忘了矢积也是有方向的,右手定则,右手手指顺着第一个向量的方向指向第二个向量的方向,默认垂直直面向上为正方向;
{
	return a.x*b.y - b.x*a.y;
}

 

然后就是点与直线的关系:可以用叉乘来判断

设点c,直线ab

所以矢量ac和cb的叉乘为0==在直线上

直线与线段的关系:

    因为是线段,所以如果还使用上面对待直线的方法的话就会把结果的范围扩大

可以使用如下方法:1,判断叉乘为0,且点c在线段ab为对角线的矩阵内

代码

bool PtOnSegment(Pt s, Pt t, Pt a) {
return !det(a-s,a-t)&&min(s.x,t.x)<=a.x &&
a.x<=max(s.x,t.x)&&min(s.y, t.y) <= a.y &&
a.y <= max(s.y, t.y);

而判断线段之间的关系时,一共有两个步骤:

①快速排斥实验:设以线段P1P2和线段Q1Q2为对角线的矩形
为M,N;若M,N 不相交,则两个线段显然不相交;所以:满
足第一个条件时:两个线段可能相交。
②跨立实验:前提:如果两线段相交,必定互相跨立。
(Q1~P1 × Q1~Q2) · (Q1~Q2 × Q1~P2) ≥ 0 表示点P1, P2在线
段Q1Q2的两侧;(P1~Q1 × P1~P2) · (P1~P2 × P1~Q2) ≥ 0表示
点Q1, Q2在线段P1P2的两侧.

若两个条件都满足,那么则两个线段相交;

求点P到线段AB的最短距离


1.判断线段PA和AB所成的夹角,如果是钝角,那么|PA|是
点到线段的最短距离。
2.判断线段PB和AB所成的夹角,如果是钝角,那么|PB|是
点到线段的最短距离。
3.线段PA和线段PB与AB所成的夹角都不为钝角,那么
点P到线段AB的距离是点P到直线AB的距离,这个距离可
以用面积法直接算出来。

 

判断点是否在三角形内,则根据面积法,如果三个顶点所与点p所形成的三个三角形的面积等于abc的面积,则p在三角形内;或者利用叉乘的方向的性质,比如p会在ac的顺时针方向,则方向是负值;

 

 

 

求多边形的面积

原理其实就是顶点相连把多边形分成三角形,然后利用叉乘来计算三角形面积,然后累加即可,此方法可适用凹凸多边形,对于凸多边形无需多言,而对于凹多边形,其实根据叉乘的正负性,是可以消掉多出的那部分三角形;

代码实现

double PolygonArea(Pt* p,int n){
double area =0;
for(int i=1;i<n-1;i++)
area+=det(p[i]-p[0],p[i+1]-p[0]);
return fabs(area/2);
}

 

pick定理

公式:S=a+b/2-1;(其中S为多边形的面积,a是多边形内的点数,b是多边形边上的点数)//把多边形放在网格图中的公式;

        把每条边当作左开右闭的区间避免重复,一条左开右闭的线
段AB上的格点数为gcd(Bx − Ax , By − Ay ) 下面有关于代码的一
些解释

int polygon_border_point_cnt(const Polygon &p) {//伪代码不要直接粘贴;
int ans = 0;
int n = p.size();//p.size()表示的是p的定点数
for (int i = 0; i < n; ++i)
ans += gcd(Abs(int(p[next(i)].x-p[i].x)),//next(i)表示i下面一个点;
Abs(int(p[next(i)].y-p[i].y)));
return ans;
}
//其实,仔细品味代码,可以看出这段代码的意思就是求这个线段在水平方向的长度和竖直长度上的值的最大公约数

 

凸包

凸包就是把给定点包围在内部的、面积最小的凸多边形首先把所有点按照从小到大排序(如果x相同,按照y从小到大排
序),删除重复点后得到序列p1, p2, ...,然后把p1和p2放到凸包
中。从p3开始,当新点在凸包“前进”方向的左边时继续,否则
依次删除最近加入凸包的点,直到新点在左边。
从左到右和从右到左各扫描一次

代码实现

int ConvexHull(Point* p,int n,Point* ch){//p为输入数组,也就是一开始存放坐标的结构体,ch就是最后得到的凸包的坐标数组
        sort(p,p+n,cmp); int m=0;
        for(int i=0;i<n;i++)
    {
        while(m>1&&det(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
        ch[m++]=p[i];
    }
    int k=m;
    for(int i=n-2;i>=0;i--){
    while(m>k&&det(ch[m-1]-ch[m-2],
    p[i]-ch[m-2])<=0) m--;
    ch[m++]=p[i];
    }
    if(n>1) m--;
    return m;

}

 

 

 

练习题

    这次的题只贴出四道题,前两道超出了本次的内容,后天补上,最后三道题太过于简单,唯一值得一写的就是对于一个直线和另一个直线相交,最多会产生(n-1)个交点,那么就会产生(n-1)+1个平面;所以可得到一个递推式f(n)=f(n-1)+(n-1)+1;fn表示n个直线所产生的平面

 

 

 

E题

Your friend to the south is interested in building fences and turning plowshares into swords. In order to help with his overseas adventure, they are forced to save money on buying fence posts by using trees as fence posts wherever possible. Given the locations of some trees, you are to help farmers try to create the largest pasture that is possible. Not all the trees will need to be used.

However, because you will oversee the construction of the pasture yourself, all the farmers want to know is how many cows they can put in the pasture. It is well known that a cow needs at least 50 square metres of pasture to survive.

Input

The first line of input contains a single integer, n (1 ≤ n ≤ 10000), containing the number of trees that grow on the available land. The next n lines contain the integer coordinates of each tree given as two integers x and y separated by one space (where -1000 ≤ x, y ≤ 1000). The integer coordinates correlate exactly to distance in metres (e.g., the distance between coordinate (10; 11) and (11; 11) is one metre).

Output

You are to output a single integer value, the number of cows that can survive on the largest field you can construct using the available trees.

Sample Input

4
0 0
0 101
75 0
75 101

Sample Output

151

首先分析可得,本题的题意是求凸包,然后把凸包的面积除以50,得到牛的个数

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
struct summer//老规矩,用结构体存坐标;
{
    int x;
    int y;
    summer (){}
    summer(int _x,int _y)
    {
        x=_x;
        y=_y;
    }
    summer operator -(const summer &b)
    {
        return  summer (x-b.x,y-b.y);
    }
}oj[10005];
int det(summer a,summer b)//用来计算叉乘
{
    return a.x*b.y-b.x*a.y;
}
summer ac[10005];
bool com(summer a,summer b)//还记得吗?在计算凸包时,是要排序的哦!;
{
    if(a.x==b.x) return a.y<b.y;
    else return  a.x<b.x;
}
int mianji(summer *p,int n)
{
    int ans=0;
    for(int i=1;i<n-1;i++)
    {
        ans+=det(p[i]-p[0],p[i+1]-p[0]);//不要随意把fabs放在这里,也许有的可以,但是对于对大多数情况都会让你WA
    }
    return  fabs((long long)ans/2);
}
int  f(summer *p,int n,summer *ch)//凸包计算公式
{
    sort(p,p+n,com);
    int m=0;
    for(int i=0;i<n;i++)
    {
        while(m>1&&det(ch[m-1]-ch[m-2],p[i]-ch[m-2])<0)
            m--;
        ch[m++]=p[i];
    }
        int k=m;
        for(int i=n-2;i>=0;i--)
        {
            while(m>k&&det(ch[m-1]-ch[m-2],p[i]-ch[m-2])<0) m--;
            ch[m++]=p[i];
        }
        if(n>1) m--;
        return  m;
}
int main()
{
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
       cin>>oj[i].x>>oj[i].y;
    }
   int re= f(oj,n,ac);
    int ans=mianji(ac,re);
    cout<<ans/50<<endl;
    return  0;
}

 

 

 

F题

Being well known for its highly innovative products, Merck would definitely be a good target for industrial espionage. To protect its brand-new research and development facility the company has installed the latest system of surveillance robots patrolling the area. These robots move along the walls of the facility and report suspicious observations to the central security office. The only flaw in the system a competitor抯 agent could find is the fact that the robots radio their movements unencrypted. Not being able to find out more, the agent wants to use that information to calculate the exact size of the area occupied by the new facility. It is public knowledge that all the corners of the building are situated on a rectangular grid and that only straight walls are used. Figure 1 shows the course of a robot around an example area.



Figure 1: Example area.


You are hired to write a program that calculates the area occupied by the new facility from the movements of a robot along its walls. You can assume that this area is a polygon with corners on a rectangular grid. However, your boss insists that you use a formula he is so proud to have found somewhere. The formula relates the number I of grid points inside the polygon, the number E of grid points on the edges, and the total area A of the polygon. Unfortunately, you have lost the sheet on which he had written down that simple formula for you, so your first task is to find the formula yourself.

Input

The first line contains the number of scenarios.
For each scenario, you are given the number m, 3 <= m < 100, of movements of the robot in the first line. The following m lines contain pairs 揹x dy�of integers, separated by a single blank, satisfying .-100 <= dx, dy <= 100 and (dx, dy) != (0, 0). Such a pair means that the robot moves on to a grid point dx units to the right and dy units upwards on the grid (with respect to the current position). You can assume that the curve along which the robot moves is closed and that it does not intersect or even touch itself except for the start and end points. The robot moves anti-clockwise around the building, so the area to be calculated lies to the left of the curve. It is known in advance that the whole polygon would fit into a square on the grid with a side length of 100 units.

Output

The output for every scenario begins with a line containing 揝cenario #i:� where i is the number of the scenario starting at 1. Then print a single line containing I, E, and A, the area A rounded to one digit after the decimal point. Separate the three numbers by two single blanks. Terminate the output for the scenario with a blank line.

Sample Input

2
4
1 0
0 1
-1 0
0 -1
7
5 0
1 3
-2 2
-1 0
0 -3
-3 1
0 -3

Sample Output

Scenario #1:
0 4 1.0

Scenario #2:
12 16 19.0

可以很清楚的明白,这也是个模板题

大概思路还是pick定理

代码

#include<iostream>
#include<cmath>
#include<string.h>
#include<stdio.h>
using namespace std;
struct summer
{
    int x,y;
    summer(){}
    summer(int _x,int _y)
    {
        x=_x;
        y=_y;
    }
    summer operator -(const summer &b)const
    {
        return summer (x-b.x,y-b.y);
    }
}oj[102];
int det(summer a,summer b)
{
    return  a.x*b.y-b.x*a.y;
}
double mianji(summer *p,int n)
{
    double ans=0;
    for(int i=1;i<n-1;i++)
    {
        ans+=det(p[i]-p[0],p[i+1]-p[0]);
    }
    return fabs(ans/2);
}
int gcd(int a,int b)
{
    return  b==0?a:gcd(b,a%b);
}
int main()
{
    int n,p=0;
    ios::sync_with_stdio(false);
    cin>>n;
    while(n--)
    {
        p++;
        int m,x=0,y=0,x1,y1;
        cin>>m;
        double ans=0;
        int ans2=0,ans1=0;
        for(int i=1;i<=m;i++)
        {
            cin>>x1>>y1;
            ans2+=gcd(abs((double)x1),abs((double)y1));
            oj[i].x=x+x1;
            oj[i].y=y+y1;
            x+=x1;
            y+=y1;
        }
        ans=mianji(oj,m);
        ans1=(ans+1-ans2/2);
        printf("Scenario #%d:\n",p);
        printf("%d %d %.1f\n",ans1,ans2,ans);
        puts("");
    }
    return  0;
}

还剩下一道D题,回来跟昨天的一个博客一起补吧,太困了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值