DAY_7(计算几何初步)

先来清点一下今天学了啥。。

今天学了叉积,多边形面积,凸包,格雷汉姆扫描,贾维斯步进。。。

今天也看了很多好文章!!!太棒了

顺便收藏一波:计算几何基础【用图来助你理解几何算法】-CSDN博客

 简直太详细了!!

下面我记一下我今天学到的:

叉积

在学之前我把它和点积弄混了,一直寻思着俩向量乘积不应该是对应横坐标乘积和纵坐标乘积之和吗?好家伙,后来看了几个博主们的介绍,我才发现,那和叉积完全是是两个东西:

叉积(又名外积,向量积) a*b=|a||b|sin\theta

θ表示向量α旋转到向量β所经过的夹角

几何意义:向量α与β所张成的平行四边形的有向面积

点积(又名内积,数量积)a*b=|a||b|cos\theta

几何意义:向量α在向量β的投影α ’ (带有方向性)与β 的长度乘积

带进坐标系的表示:p_1\times p_2=det\bigl(\begin{smallmatrix} x_1& x_2\\ y_1 &y_2 \end{smallmatrix}\bigr)=x_1y_2-x_2y_1=-p_2\times p_1

叉积的方向性:

对于P_1\times P_2:

  • >0  P1在P2的顺时针方向上
  • =0  P1和P2同线
  • <0  P1在P2逆时针方向(counterclockwise)

现在我们来解决三个好问题:

1、给定具有共同端点P0的两个有向线段P0P1和P0P2,那么如何判断P0P1和P0P2的位置关系?

——向量做叉乘运算!!

P_0P_1\times P_0P_1=(x_1-x_0)(y_2-y_0)-(x_2-x_0)(y_1-y_0)

2、给定两个有向线段P0P1和P0P2,如果先沿着P0P1方向走,然后沿着P0P2走,那么在P1点该向左还是向右转?

——向量做叉乘运算!!

P_0P_1\times P_1P_2=(x_1-x_0)(y_2-y_1)-(x_2-x_1)(y_1-y_0)

3、给定两个向量P1P2和P3P4,如何判断它们是否相交

两种情况:

对于第一种:

进行两次跨立实验:

\left.\begin{matrix} sgn(P_1P_2\times P_1P_3 )*sgn(P_1P_2\times P_1P_4)<0 \\ sgn(P_3P_4\times P_3P_2 )*sgn(P_3P_4\times P_3P_1)<0 \end{matrix}\right\}

上面推出:P3,P4在P1,P2两侧

下面推出:P1,P2在P3,P4两侧

最后推出:P1,P2和P3,P4相交

对于第二种:

 判断每个向量积是否有等于0的

再加一个判断是否悬空的情况(垂直但不接触):

若min(x1,x2)<=x3<=max(x1,x2)且min(y1,y2)<=y3<=max(y1,y2),则相交

 多边形面积

众所周知,对于一个多边形,假设有n个顶点,可以将其分成n-2个三角形,然后对其求和,三角形面积之和即为多边形面积之和

对于“无敌的”计算机来说,用海伦公式求一定很方便,不过既然学了叉积,我们就可以用叉积来求

最后::S=\frac{1}{2}\sum_{i=1}^{n}\begin{vmatrix} X_i& Y_i \\ X_{i+1}&Y_{i+1} \end{vmatrix}(i=1,2,...,N)(N+1->1)

凸包问题

凸包问题:多个点,用一条线将所有点全部围在里面

1、格雷汉姆扫描法

(1)找到y最小的点作为基点

(2)依据角度大小顺序sort其余点(与x正轴升序排序)

2、贾维斯步进法

每次寻找角度最小的点即可

额额。。那个。。写的有点水。。这边建议去看人家博客!!

(再来安利一波)计算几何基础【用图来助你理解几何算法】-CSDN博客

下面上今日题目:

P1183 多边形的面积

这个第一题还是比较水的,就简单用个叉积就OK,基本不用动脑子(突然不明白为啥我还写了一个小时。。。)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=205;
int n;
int x[N],y[N];
int ans;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>x[i]>>y[i];
	x[n+1]=x[1];
	y[n+1]=y[1];
	for(int i=1;i<=n;i++)
	ans+=(x[i+1]*y[i]-x[i]*y[i+1]);
	cout<<abs(ans)/2<<endl;
}

P1355 神秘大三角

两个方法,一个是面积法,当pab,pac,pbd三个三角形面积之和大于三角形abc面积时,p在外面,等于则在里面,当然了首先要判断是否有在顶点的情况,还有得先判断是否有面积等于0,有的话就在线段上。

面积法

代码:

#include<bits/stdc++.h>
using namespace std;
double xx[4],yy[4];
double x,y;
char c;
double lonlen(double x1,double y1,double x2,double y2)
{
	return pow((x1-x2),2)+pow((y1-y2),2);
}
int main()
{
	cin>>c>>xx[1]>>c>>yy[1]>>c;
	cin>>c>>xx[2]>>c>>yy[2]>>c;
	cin>>c>>xx[3]>>c>>yy[3]>>c;
	cin>>c>>x>>c>>y>>c;
	if(x==xx[1]&&y==yy[1]||x==xx[2]&&y==yy[2]||x==xx[3]&&y==yy[3])
	{
		cout<<4<<endl;
		return 0;
	}
	double a,b,c;
	a=sqrt(lonlen(xx[1],yy[1],xx[2],yy[2]));//AB
	b=sqrt(lonlen(xx[2],yy[2],xx[3],yy[3]));//BC
	c=sqrt(lonlen(xx[3],yy[3],xx[1],yy[1]));//AC
	double p=(a+b+c)/2;
	double s,s1,s2,s3;
	s=(int)sqrt(p*(p-a)*(p-b)*(p-c))*100;
	double ap,bp,cp; 
	ap=sqrt(lonlen(xx[1],yy[1],x,y));//PA
	bp=sqrt(lonlen(xx[2],yy[2],x,y));//PB
	cp=sqrt(lonlen(xx[3],yy[3],x,y));//PC
	p=(ap+bp+a)/2;
	s1=(int)sqrt(p*(p-ap)*(p-bp)*(p-a))*100;//Spab
	p=(b+bp+cp)/2;
	s2=(int)sqrt(p*(p-b)*(p-bp)*(p-cp))*100;//Spbc
	p=(ap+c+cp)/2;
	s3=(int)sqrt(p*(p-ap)*(p-c)*(p-cp))*100;//Spac
	if(s1+s2+s3>s)
	{
		cout<<2<<endl;
		return 0;
	}
	if(!s1||!s2||!s3)
	{
		cout<<3<<endl;
		return 0;
	}
	cout<<1<<endl;
	return 0;
}
 叉积法

先求判断一波P点是否在顶点上,然后再求P点和三个顶点的叉积,要是存在叉积为0,且另外俩叉积同号则在线段上,如果三个叉积都同号则在里面,否则在三角形外面

代码:

#include<bits/stdc++.h>
using namespace std;
struct triangle
{
	int xtri;
	int ytri;
}point[4];
int x,y;
int a,b,c;
char s;
int check(int x,int y)
{
	for(int i=1;i<=3;i++)
	if(x==point[i].xtri&&y==point[i].ytri)
	return 4;
	a=(x-point[1].xtri)*(y-point[2].ytri)-(x-point[2].xtri)*(y-point[1].ytri);
	b=(x-point[2].xtri)*(y-point[3].ytri)-(x-point[3].xtri)*(y-point[2].ytri);
	c=(x-point[3].xtri)*(y-point[1].ytri)-(x-point[1].xtri)*(y-point[3].ytri);
	if(!a&&b*c>0||!b&&a*c>0||!c&&a*b>0)
	return 3;
	if(a>0&&b>0&&c>0||a<0&&b<0&&c<0)
	return 1;
	return 2;
}
int main()
{
	for(int i=1;i<=3;i++)
	cin>>s>>point[i].xtri>>s>>point[i].ytri>>s;
	cin>>s>>x>>s>>y>>s;
	cout<<check(x,y)<<endl;
}

P3744 李彬的几何

一道水水水水水的蓝题。。。

一个凸多边形要变成凹多边形,我们可以抽象成一个绷直的绳子,现在将它往上拉,它就会凸起,往下拉,就会凹陷,再将这个绳子带进一个凸多边形,能发现:两个点之间若只有一个点,那么两个点作为端点,中间的点作为活动点可以上下移动,根据垂线段最短的原理,让这个多边形凹下去的条件之一就可以移动这个点到两个端点所连直线的下面,最短距离即为垂线段距离,那么这一题也就是求出某点到它左右点所连直线的垂直距离,因为所有点都可以动,所以最后结果需要除2

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
double eps=1e-4;
int n;
double d=1e9+5;
struct dd
{
	double x;
	double y;
	dd operator - (const dd &r)const
	{
		return (dd){x-r.x,y-r.y};
	}
}point[N];
double cross(dd a,dd b)
{
	return a.x*b.y-a.y*b.x;
}
double dis(dd m,dd n)
{
	return sqrt(pow(m.x-n.x,2)+pow(m.y-n.y,2));
}
double move(dd a,dd b,dd c)
{
	return abs(cross(b-a,b-c))/dis(a,c);
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>point[i].x>>point[i].y;
	point[n+1].x=point[1].x;
	point[n+1].y=point[1].y;
	point[0].x=point[n].x;
	point[0].y=point[n].y;
	for(int i=1;i<=n;i++)
	d=min(d,move(point[i-1],point[i],point[i+1]));
	cout<<d/2<<endl;
	return 0;
}

P2742 [USACO5.1] 圈奶牛Fencing the Cows /【模板】二维凸包

二维凸包的板子,格雷汉姆扫描

直接上代码,有注释

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n;
double ans;
int top;
struct dd
{
	double x;
	double y;
	dd operator - (const dd &r)const
	{
		return (dd){x-r.x,y-r.y};
	}//重载减法 
}stk[N],point[N];//stk存凸包顶点坐标,point存原始坐标 
double cross(dd a,dd b)
{//叉积 
	return a.x*b.y-a.y*b.x;
}
double dis(dd a,dd b)
{//两点距离 
	return sqrt(pow(a.x-b.x,2)+pow(a.y-b.y,2));
}
bool cmp(dd p1,dd p2)
{
	double k=cross(p1-point[1],p2-point[1]);
	if(k>0)
	return true;
	if(k==0&&dis(point[0],p1)<dis(point[0],p2))
	return true;
	return false;
}//按照夹角的大小排序 
int main()
{
	cin>>n;
	int o;
	for(int i=1;i<=n;i++)
	{
		cin>>point[i].x>>point[i].y;
		if(i!=1&&point[i].y<point[1].y||i!=1&&point[i].y==point[1].y&&point[i].x<point[1].x)
		{//去重、选出最小点 
			dd mid;
			mid=point[1];
			point[1]=point[i];
			point[i]=mid;
		}
	}
    stk[1]=point[1];//最小点 
	sort(point+2,point+1+n,cmp);//排序 
    top=1;//手写栈 
    for(int i=2;i<=n;i++)
	{
        while(top>1&&cross(stk[top]-stk[top-1],point[i]-stk[top])<=0)
		top--;
		top++;
        stk[top]=point[i];
    }
    stk[top+1]=point[1];//兜兜转转,回到起点 
	for(int i=1;i<=top;i++)
	ans+=dis(stk[i],stk[i+1]);
	cout<<fixed<<setprecision(2)<<ans<<endl;
	return 0;
}

P1257 平面上的最接近点对&&P1429 平面最近点对(加强版)

用一个代码成功水了两题,芜湖~~

分治思想,先对原始数组进行在x轴上的排序,然后遍历左右两边,寻找左右两边的最近点,然后比较区间内点和mid的距离,若有可能比dmin小,那么加入另一数组进行查询

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n;
struct Point
{
	double x;
	double y;
}p[N];
int tem[N];
bool cmpx(const Point &a,const Point &b)
{
	return a.x<b.x;
}
bool cmpy(const int &a,const int &b)
{
	return p[a].y<p[b].y;
}
double dis(int a,int b)
{
	return sqrt(pow(p[a].x-p[b].x,2)+pow(p[a].y-p[b].y,2));
}
double min(double a,double b)
{
	return a>b?b:a;
}
double minndistance(int l,int r)
{
	if(r==l)
	return (1<<30);
	if(r-l==1)
	return dis(l,r);
	int mid=(r+l)>>1;
	double d1=minndistance(l,mid);
	double d2=minndistance(mid+1,r);
	double d=min(d1,d2);
	int cnt=0;
	for(int i=l;i<=r;i++)
	if(abs(p[i].x-p[mid].x)<d)
	tem[++cnt]=i;
	sort(tem+1,tem+1+cnt,cmpy);
	for(int i=1;i<=cnt;i++)
	for(int j=i+1;j<=6 && j<=cnt;j++)
	{
		if(p[tem[j]].y-p[tem[i]].y>=d)
		break;
		d=min(d,dis(tem[i],tem[j]));
	}
	return d;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>p[i].x>>p[i].y;
	sort(p+1,p+1+n,cmpx);
	cout<<fixed<<setprecision(4)<<minndistance(1,n)<<endl;
}

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值