目录
计算几何,真的是无奈。。。现在许多计算几何的题目都很恶心
@
叉积
如何判断以\(O\)为基点,以\(A\)到\(B\)是顺时针还是逆时针?
暴力判断
这里,我们引入一个叫叉积的东西,这个东西原本是一个大佬运用在三维坐标系中三个向量相乘后得到的一个向量(三维叉积),但是在二维平面内得到的一个数字(二维叉积)竟然又具备了多个性质。
A(x1,y1),B(x2,y2)
则叉积为(x1*y2)-(x2*y1),当此式大于0时,A逆时针到B,小于0,反之,等于0,O,A,B共线。
当然,如果不是以O点为基准,是以C(x3,y3)的话,就用A,B的x,y坐标减去C的x,y坐标,化成以O点为基准就行了。
由于叉积很难证,这里给出B站一个大佬的视频(我在B站学数学!),形象理解叉积,一个一个往下看:
1
2
3
4
5
5(补)
6
耳朵好的人可以开弹幕加两倍数来学习呦。
也许,你会说并没有讲,但是有一张图片会很直观
这里也就解释了叉积就是一个二次行列式,当叉积的值大于\(0\),就是\(i\) \(hat(a,b)\)逆时针到\(j\) \(hat(c,d)\),小于\(0\),说明坐标轴翻转了,所以\(i\) \(hat(a,b)\)顺时针到\(j\) \(hat(c,d)\),\(0\)就是降维打击QMQ。
而且,叉积还有个几何意义就是线性变换后\(i,j\) \(hat\)形成的平行四边形的面积,所以三点共线自然就是\(0\)了。
因此也就可以解释两个点的叉积的绝对值除以\(2\)等于两个点于原点形成的三角形的面积。
当然,下面许多地方会用到叉积,也许你会发现代码里面的叉积与我所讲的判断方法不同,但是你会发现还是对的,因为叉积你改动里边参数的位置只会改变正负,所以做几何题叉积一定要熟手。
多边形的面积
题目描述
【题意】
在一个平面坐标系上随意画一条有n个点的封闭折线(按画线的顺序给出点的坐标),保证封闭折线的任意两条边都不相交。最后要计算这条路线包围的面积。
(我搞不定相交的情况,同学们课后可以研究,有结果告诉我)
【输入格式】
第一行整数 n (3 <= n <= 1000),表示有n个点。
下来n行,每行两个整数x(横坐标)和y(纵坐标),表示点坐标(-10000<x,y<=10000)。
【输出格式】
一行一个实数,即封闭折线所包围的面积(保留4位小数)。
【样例1输入】
4
2 1
5 1
5 5
2 5
【样例1输出】
12.0000
【样例2输入】
5
2 1
5 1
3 2
5 3
2 3
【样例2输出】
4.0000
因为菜OJ太容易炸了,所以只能薅体面了
先考虑凸多边形:
可以看出只要选一个点然后以这个点分割成一堆三角形就行了,而且用叉积都是一个方向。
那么我们再考虑这个情况:
我们会发现其中不同方向的叉积消掉,很棒棒。
多画几个图会发现可以。
#include<cstdio>
#include<cstring>
#define N 1100
using namespace std;
int n;
struct node{double x,y;}list[N];
double mu(node x,node y,node z)
{
double x1=(x.x-z.x),y1=(x.y-z.y),x2=(y.x-z.x),y2=(y.y-z.y);
return x1*y2-x2*y1;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lf%lf",&list[i].x,&list[i].y);
double ans=0;
for(int i=3;i<=n;i++)ans+=mu(list[i],list[i-1],list[1]);//直接求
if(ans<0)ans=-ans;
printf("%.4lf",ans/2.0/*三角形面积要除以2*/);
return 0;
}
判断线段相交
题目描述
【题意】
有n条线段(编号为1~n),按1~n的顺序放在二维坐标系上(就是先放1号,再放2号……),
要求输出最上面的那些线段的编号(就是没有其他线段压在它上面的那些线段)
【输入格式】
第一行第一个数n( 1 <= n <= 10000)表示这组数据有n条线段。
下来n行,每行两个坐标,表示第i条线段的两个端点。
【输出格式】
一行。输出最上面的线段的编号(从小到大)。相邻两个编号用空格隔开,最后一个编号没有空格。
【样例1输入】
5
1 1 4 2
2 3 3 1
1 -2.0 8 4
1 4 8 2
3 3 6 -2.0
【样例1输出】
2 4 5
【样例2输入】
3
0 0 1 1
1 0 2 1
2 0 3 1
【样例2输出】
1 2 3
我们来看一看普通的相交情况:
也就是说我们可以用叉积判断是一条线段的左右端是否在另一条线段的左右两侧。
if(mu(y.x,x.x,x.y)*mu(x.x,y.y,x.y)>0 && mu(x.x,y.x,y.y)*mu(y.x,x.y,y.y)>0)return true;
//普通的相交情况,当然,变量名比较狗。
但是,还有一种特殊情况,也就是叉积为0,共线。
这个就比较恶心了,我们可以暴力判断一波。
然后就得出了这个优(e)美(xin)的AC代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 21000
using namespace std;
struct dian{double x,y;};
struct bian{dian x,y;}list[N];
int n;
inline double mymin(double x,double y){return x<y?x:y;}
inline double mymax(double x,double y){return x>y?x:y;}
inline double mu(dian x,dian y,dian z)//x->y
{
double x1=(x.x-z.x),y1=(x.y-z.y),x2=(y.x-z.x),y2=(y.y-z.y);
return x1*y2-x2*y1;
}
inline bool pd(dian x1,dian x2,dian y1){return (mymin(x1.x,x2.x)<=y1.x && mymax(x1.x,x2.x)>=y1.x && mymin(x1.y,x2.y)<=y1.y && mymax(x1.y,x2.y)>=y1.y);}
inline bool check(bian x,bian y)
{
if(mu(y.x,x.x,x.y)*mu(x.x,y.y,x.y)>0 && mu(x.x,y.x,y.y)*mu(y.x,x.y,y.y)>0)return true;
//普通情况
if((mu(x.x,x.y,y.x)==0 && pd(x.x,x.y,y.x)) ||
(mu(x.x,x.y,y.y)==0 && pd(x.x,x.y,y.y)) ||
(mu(y.x,y.y,x.x)==0 && pd(y.x,y.y,x.x)) ||
(mu(y.x,y.y,x.y)==0 && pd(y.x,y.y,x.y)))return true;
//暴力判断不同的相交情况
return false;
}
bool bo[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lf%lf%lf%lf",&list[i].x.x,&list[i].x.y,&list[i].y.x,&list[i].y.y);
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(check(list[i],list[j])){bo[i]=true;break;}
}
}
//O(n^2)暴力判
for(int i=1;i<=n;i++)
{
if(!bo[i])printf("%d ",i);
}
printf("\n");
return 0;
}
二维凸包
怎么可能是三维的,开玩笑,我超勇的。
那么,我们用的二维凸包求法是Graham扫描法。
题目描述
【题意】
在一个平面坐标系上有n个点,用笔画一个多边形,使得多边形包含这n个点(点在多边形的边上也算包含)。
求多边形的最小周长。
【输入格式】
第一行整数 n (1 <= n <= 1000),表示有n个点。
下来n行,每行两个整数x(横坐标)和y(纵坐标),表示点坐标(-10000<=x,y<=10000)。
【输出格式】
一行一个实数,即多边形的最小周长(保留4位小数)。
【样例1输入】
4
2 1
5 1
5 5
2 5
【样例1输出】
14.0000
【样例2输入】
5
2 1
5 1
3 2
5 3
2 3
【样例2输出】
10.0000
那么,我们如何找最小的周长,想象一下,拿一个橡皮筋,在一个钉满钉子的一片区域,松开,这个凸多边形的周长就是最短的周长了,许多凸包的半裸题都一定要有这种想法!!!
那么怎么求这个凸多边形就是个问题。
首先肯定,最左边的点肯定在凸包上,那么我们就把他放在第一个位置,这个点有个至关重要的性质。
设最左边的点为x,另外的两个点y,z
不存在一组y,z,使得y->x->z的角度大于等于180,这样就使得如果全以x为基准点,y->z是逆时针,而z->k也是逆时针,那么y->k也是逆时针,这为我们的一个排序提供了保障。
逆时针排序
代码有详解
double eps=1e-10;//精度!!!
inline bool cmp(dian x,dian y)
{
double tt=mu(x,y,list[1]);
if(tt>eps)return true;//逆时针排序
else if(fabs(tt)<=eps && dis(list[1],x)<dis(list[1],y))return true;//共线就距离近的进来。
return false;
}
模拟过程
LOOK
我们讨论两种情况(\(x\)为\(list[top-1]\),\(y\)为\(list[top]\),\(z\)为待判断的数字)
逆时针旋转说明\(z\)在凸包内
反之。
代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define N 2100
using namespace std;
struct dian{double x,y;}list[N];
int sta[N],n;
inline double mu(dian x,dian y,dian z)//叉积
{
double x1=(x.x-z.x),y1=(x.y-z.y),x2=(y.x-z.x),y2=(y.y-z.y);
return (x1*y2)-(x2*y1);
}
inline double dis(dian x,dian y){return sqrt((y.x-x.x)*(y.x-x.x)+(y.y-x.y)*(y.y-x.y));}//距离
inline void zwap(dian &x,dian &y){dian z=x;x=y;y=z;}
double eps=1e-10;
inline bool cmp(dian x,dian y)
{
double tt=mu(x,y,list[1]);
if(tt>eps)return true;//逆时针
else if(fabs(tt)<=eps && dis(list[1],x)<dis(list[1],y)/*长度小的排前面*/)return true;//共线
return false;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&list[i].x,&list[i].y);
if(list[i].y<list[1].y || (list[i].y==list[1].y && list[i].x<list[1].x))zwap(list[1],list[i]);//找左边的点
}
sort(list+2/*-------有人注意到这里是加2吗-----------*/,list+n+1,cmp);
sta[1]=1;sta[2]=2;sta[0]=2;
for(int i=3;i<=n;i++)
{
while(sta[0]>1/*边界条件*/ && mu(list[sta[sta[0]]],list[i],list[sta[sta[0]-1]])<=eps/*共线也踢人*/)sta[0]--;
sta[++sta[0]]=i;
}
double ans=0;
for(int i=2;i<=sta[0];i++)ans+=dis(list[sta[i]],list[sta[i-1]]);
if(sta[0]>2)ans+=dis(list[sta[sta[0]]],list[1]);
printf("%.4lf\n",ans);//计算周长
return 0;
}
半平面交
这就很恶心了。。。
题目描述
【题意】
在一个有限大(-10 0000<=x,y<=10 0000)的平面坐标系上有n个半平面(注意有限的),每个半平面给出一条有向线段(x1,y1)——>(x2,y2)。
每个半平面的有效区域都是左侧(包括此直线)。求这n个半平面的交的面积。
【输入文件】
第一行一个整数n(1 <= n <= 2 0000)
下来n个半平面。每个半平面一行四个整数x1,y1,x2,y2。(-100000 <= x1,y1,x2,y2 <= 100000)
【输出文件】
一行,输出n个半平面的交的面积(保留一位小数),如果有效面积不存在则输出"0.0"。
【样例1输入】
3
1 1 9 9
5 10 4 10
0 3 0 2
【样例1输出】
50.0
【样例2输入】
4
0 0 10 0
10 0 10 10
10 10 0 10
11 11 11 0
【样例2输出】
0.0
判断一个点是否在这条直线的半平面
我们把直线变成线段再看:
当然,\(0\)就是在,因为包括直线。
直线有角度??
当然,这里指的直线的角度就是直线与\(x\)轴所形成的角度。
给出我们求的角度的例子。
当然,C++是弧度制的,也就是说一个圆的角度是\(2π\),转换成角度只需要\(/π*180\)
那么怎么求弧度,也许你可以先去百度百科了解一下三角函数中的\(tan\)函数,正切函数,对边比邻边。
那么我们还引入一个概念:反正切函数,就是给你对边比邻边的比值(比值可以为负),让你求角度,也就是\(atan\)函数,但是\(atan\)函数返回的角度值只能是\((-90°,90°)\),所以比较局限,所以又出了一个\(atan2\)的函数,可以扔进去两个数值,一个对边的长度,一个邻边的长度,正负皆可。
那么角度的问题我们也解决了,当然,角度我们可以不转,为了调试可以转一转。
两线交点出奇迹
难道我们求两线交点就只能列一个二元一次方程?
不,我们可以用叉积!!!
看如下图:
如何求\(\frac{AP}{CP}\)?
我们做一条垂线:
我们\(\frac{AE}{BF}=\frac{S▲ABD}{S▲CBD}\),\(∠APE=∠CPF,∠AEP=∠CFP\),所以\(▲AEP∽▲CFP\),所以\(\frac{AP}{CP}=\frac{AE}{CF}=\frac{S▲ABD}{S▲CBD}\)
而这个面积是可以用叉积来求的。
我们设\(t1,t2\)分别为以\(C\)为基准点旋到\(A,B\)的叉积。
两种情况:
1
此处\(t1<0,t2>0\)
\(P\)点坐标?
列波式子(求\(x\)坐标,\(y\)坐标一样,在这里推导过程省略原因,多用相似三角形):
\[x1+\frac{-(x2-x1)*t1}{t2-t1}\]
\[=\frac{x1(t2-t1)-(x2-x1)*t1}{t2-t1}\]
\[=\frac{x1*t2-x2*t1}{t2-t1}\]
\[=\frac{t1*x2-t2*x1}{t1-t2}\]
当然,死记硬背也没问题:\(122112\)。
2
那么这个的长这样:
在此\(t1<0,t2<0\)
\[x1+\frac{-(x2-x1)*t1}{t2-t1}\]
\[=\frac{x1(t2-t1)-(x2-x1)*t1}{t2-t1}\]
\[=\frac{x1*t2-x2*t1}{t2-t1}\]
\[=\frac{t1*x2-t2*x1}{t1-t2}\]
等会,这个很眼熟!!!
跟上面一样,所以用这个就行了。
inline dian jd(line x,line y)//怎么可能用inline
{
double t1=mu(x.x,y.x,y.y),t2=mu(x.y,y.x,y.y);
return dian((t1*x.y.x-t2*x.x.x)/(t1-t2),(t1*x.y.y-t2*x.x.y)/(t1-t2));
}
不存在半平面面积的例子
我们按角度把直线分成四个部分,当然,与\(x,y\)重合或平行的直线我们暂时不理他们,可以视他们存在于两种情况,这些例子只是帮助理解:
- \(-180°<x<-90°\)
- \(-90°<x<0°\)
- \(0°<x<90°\)
- \(90°<x<180°\)
例子1号
当坐标系中只有第\(1,4\)部分的直线的话,那么面积无限,第\(2,3\)部分也是如此。
这个很好想。
但同时这个例子也干掉了许多不正常的情况。
例子2号
有两条的角度的正负不同,才可能有解。
例子3号
一定要把一些角度相同的直线筛掉,直留一条!!!
当然,如果角度的绝对值想加为\(180°\)的话(另类平行),就一定要有两条直线框住这两条直线的半平面交部分,否则也是无限的。
模拟
现在我们在假设有解的情况下正式开始吧。
假设现在已经有个比较完整的架构了:
现在有一条直线出现了!
但是我们发现A点也就是目前\(list[top]\)与\(list[top-1]\)的交点不在这条直线的半平面,而且\(list[top]\)这条直线的角度小于这条直线,所以我们可以把\(top--\),因为\(list[top]\)做不了任何贡献了。
但是难道只是踢\(top\)就可以了吗?
LOOK:
\(head\)也要踢。
那么是不是这样就行了?
不行,在结束之后还要用\(list[head]\)去踢\(top\)的值。
那么半平面交就结束了。
当然,还有个最大的例子将在最后放出来。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 21000
#define MAXN 100000
using namespace std;
double eps=1e-8;
struct dian
{
double x,y;
dian(double xx=0.0,double yy=0.0){x=xx;y=yy;}
}pi[N];//点
struct line
{
dian x,y;double agr;
void set(double x1,double y1,double x2,double y2){x.x=x1;x.y=y1;y.x=x2;y.y=y2;agr=atan2(y2-y1,x2-x1);/*求角度*/}
}ss[N],li[N];int head,tail,n,tp;
inline double mu(dian x1,dian x2,dian y){return (x1.x-y.x)*(x2.y-y.y)-(x2.x-y.x)*(x1.y-y.y);}//叉积
inline dian jd(line x,line y)
{
double t1=mu(x.x,y.x,y.y),t2=mu(x.y,y.x,y.y);
return dian((t1*x.y.x-t2*x.x.x)/(t1-t2),(t1*x.y.y-t2*x.x.y)/(t1-t2));
}//求交点
inline bool safe(dian x,line y){return mu(y.x,x,y.y)<=eps;}//在不在此半平面
inline bool cmp(line x,line y)//角度排序
{
if(x.agr<y.agr)return true;
else if(fabs(x.agr-y.agr)<=eps && safe(x.x,y)==true/*如果同一个角度那么就判断哪个直线的半平面比较小(在有限范围内)*/)return true;
return false;
}
void HPI()
{
sort(ss+1,ss+n+1,cmp);
tp=1;for(int i=2;i<=n;i++)if(ss[i].agr-ss[i-1].agr>eps)ss[++tp]=ss[i];//去重
li[head=1]=ss[1];li[tail=2]=ss[2];n=tp;
for(int i=3;i<=n;i++)
{
while(head<tail && safe(jd(li[tail],li[tail-1]),ss[i])==false)tail--;//踢队尾
while(head<tail && safe(jd(li[head],li[head+1]),ss[i])==false)head++;//踢队头
li[++tail]=ss[i];//加入
}
while(head<tail && safe(jd(li[tail],li[tail-1]),li[head])==false)tail--;//再来一次
if(tail-head<2){printf("0.0\n");return ;}//无解
int pl=0;for(int i=head;i<tail;i++)pi[++pl]=jd(li[i],li[i+1]);pi[++pl]=jd(li[head],li[tail]);//求出凸多边形
double ans=0;
for(int i=1;i<=pl;i++)ans+=mu(pi[i-1],pi[i],pi[1]);//算面积
if(ans<0)ans=-ans;
printf("%.1lf\n",ans/2.0);
}
int main()
{
scanf("%d",&n);
ss[1].set(MAXN,MAXN,-MAXN,MAXN);ss[2].set(-MAXN,MAXN,-MAXN,-MAXN);
ss[3].set(-MAXN,-MAXN,MAXN,-MAXN);ss[4].set(MAXN,-MAXN,MAXN,MAXN);n+=4;//添加四条直线,因为题目添加了,所以不用考虑无限的情况
for(int i=5;i<=n;i++)
{
double x1,y1,x2,y2;scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
ss[i].set(x1,y1,x2,y2);
}
HPI();
return 0;
}
最大的例子
注:主要用四个部分来证,而与x或y轴平行的线可以归到两个部分都可以。(没试过QAQ)
我们都知道很容易出现一种情况,就是一条线的的半平面与现在的半平面一点都没重合。
现在举一些例子:
两条第2部分的直线,一条第4部分的之前
可以看出,就是这种情况,当然,我们通过此图或画图可以看出一个更一般的情况,部分3能淘汰只由部分1组成的半平面,部分4只能淘汰只由部分2组成的半平面,当然,复合平面的话从某种意义将也就是遵循这种情况。
但是,我们发现当代码里面发现了这种情况并没有及时的break,而且还对了!
这就很迷了。
这里我们先列个问题解决:
部分3淘汰了只由部分1组成的半平面后(还会留个部分1的list[head]),部分4还能构成一个三角形吗?
首先,我们枚举部分1、3、4如何才能构成一个三角形,发现只有这样:
也就是部分3与部分1重合的半平面向右上指。
也就是说部分3中以y为x函数的斜率小于部分1的。
同时,几个第1部分的直线的半平面交让我们看看
很明显,半平面的朝向一般是右下角,而我们也看出来,角度最小的直线(蓝色)的边边永远会作为半平面的一个边界(而且是最上面的那个边界),那么当一个部分3淘汰完要与它组成三角形时,又必须从下面穿进这个直线的半平面,想想就知道,肯定会有半平面相交,所以这种情况是淘汰不掉的。
(就算是两条第3部分的话,列一下也会发现不行,部分2的也可以这么)
那么,你通过多个例子会发现其实根本就没问题,当然,枚举也是挺麻烦的,考场上尽量记结论,否则很麻烦(除非你有更快证法)。
旋转卡壳
这种东西,你只要理解做法就行了,因为这种东西十分的玄。
题目描述
【题意】
给出n个点的坐标,求最远两点间的距离。
【输入格式】
第一行一个整数n(2 ≤ n ≤ 50000)。
下来n行,每行两个实数x和y表示点坐标(x,y<=|10^6|)。
【输出格式】
一行一个实数,表示最远两点间的距离的平方(保留2位小数)。
【样例输入】
5
0 0
0 5
5 0
5 5
2 0
【样例输出】
50.00
凸包
我们首先可以知道最长的距离肯定在凸包上,如果不在,你可以连线最近的两个在凸包上的点,总有一条是大于这个线段的。
旋转!跳跃!
那么,在凸包上我们还能怎么做?O(n^2)
那么这就很伤了呀。
但是还是有大佬发明了一个\(O(n)\)的算法。
我们现在准备两条平行线,卡住这个凸多边形。
来源于https://jvruo.com/archives/79/
当然,卡住的情况有三种:
- 点对点。
- 点对边
- 边对边
由于点对点比较难,后面旋转的时候会旋转成点对边,而且边对边可以拆成两个点对边,所以我们只讨论点对边的情况,也就是这种情况:
但是如何求对踵点?选一条边,存在一个点使得构成的三角形面积最大的就是对踵点,同时也就是点对边。
通过自己感性的理解以及加以画图,应该可以理解为什么是对踵点才是最长的了。
而且我们会发现,我们逆时针模拟的话,对踵点也是绕着凸包逆时针旋转。
有人证明过,对踵点对数量小于\(\frac{3n}{2}\)
错误示范
为什么模拟点对边可以模拟出所有的对踵点。
举一个简单的例子:
你会发现以\(AF,AE\)为边的话,\(AB\)这对对踵点无法找到,这就很气人了,我也以为旋转卡壳是错的。
但是调试发现\(BD\)就会以\(A\)为对踵点了。
自己画了几个图发现,对踵点至少会被点到一次,最多被点到四次。
代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define N 111000
using namespace std;
double eps=1e-10;
struct dian{double x,y;}aa[N],sta[N];int n,top;
inline double mu(dian x1,dian x2,dian y){return (x1.x-y.x)*(x2.y-y.y)-(x2.x-y.x)*(x1.y-y.y);}//叉积
inline double dis(dian x,dian y){return (x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y);}//距离,不用开根,这题开根会掉精
inline bool cmp(dian x,dian y)//排序
{
double tt=mu(x,y,aa[1]);
if(tt>eps)return true;
else if(fabs(tt)<=eps && dis(aa[1],x)<dis(aa[1],y))return true;
return false;
}
void tubao()//凸包
{
sort(aa+2,aa+n+1,cmp);
top=2;sta[1]=aa[1];sta[2]=aa[2];
for(int i=3;i<=n;i++)
{
while(top>1 && mu(sta[top],aa[i],sta[top-1])<=0)top--;
sta[++top]=aa[i];
}
}
inline double mymax(double x,double y){return x>y?x:y;}
void ka()
{
double ans=0;
sta[++top]=sta[1];/*收尾相连*/int now=2;//特判点数1与2的情况
for(int i=1;i<top;i++)
{
while(mu(sta[i+1],sta[now+1],sta[i])>mu(sta[i+1],sta[now],sta[i]))//叉积求面积,这里需要处理好旋转方向,保证面积大于0
{
now++;if(now==top)now=1;
}
ans=mymax(ans,dis(sta[now],sta[i]));
}
printf("%.2lf\n",ans);//dis没开根
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&aa[i].x,&aa[i].y);
if(aa[i].x<aa[1].x || (aa[i].x==aa[1].x && aa[i].y<aa[1].y)){dian zjj=aa[1];aa[1]=aa[i];aa[i]=zjj;}
}
tubao();//凸包
ka();//旋转卡壳
return 0;
}
一些练习
作者太弱了,只有两道题吧QAQ。
1
题目描述
【题目描述】
给定平面上N个点,你需要计算以其中4个点为顶点的正方形的个数。注意这里的正方形边不一定需要和坐标轴平行。
【输入文件】
第一行一个数N,以下N个点的坐标。
【输出文件】
一个数正方形的个数。
【样例输入】
7
0 0
0 1
1 0
1 1
1 2
2 1
2 2
【样例输出】
3
【数据规模】
对于20%的数据,满足1≤N≤20;
对于l00%的数据,满足1≤N≤500,-50≤X[i],Y[i]≤50,点不会重叠。
暴力枚举两个点,然后一直暴力一直爽。
#include<cstdio>
#include<cstring>
using namespace std;
int a[610][2],n,ans;
bool bk[610][610];
void work(int x,int y)
{
int xx=(a[y][0]-a[x][0]),yy=(a[y][1]-a[x][1]);
int x1=a[x][0]-yy,y1=a[x][1]+xx,x2=a[y][0]-yy,y2=a[y][1]+xx;
if(bk[x1][y1] && bk[x2][y2])ans++;
x1=a[x][0]+yy,y1=a[x][1]-xx,x2=a[y][0]+yy,y2=a[y][1]-xx;//暴力算出两个三角形,bool矩阵暴力判
if(bk[x1][y1] && bk[x2][y2])ans++;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x,y;scanf("%d%d",&x,&y);x+=150;y+=150;
a[i][0]=x;a[i][1]=y;bk[x][y]=true;
}
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)work(i,j);
}
printf("%d\n",ans/4);//每个矩阵重复算四次
return 0;
}
2
翻译:
给你一堆构成凸包的点,然后以原点为基准,逆时针输出所有的点。
无三点共线,无其他点在x,y轴。
暴力一直爽,一直暴力一直爽。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
double eps=1e-8;
struct dian{double x,y;}a[100];
double pd(dian x1,dian x2,dian y){return (x1.x-y.x)*(x2.y-y.y)-(x2.x-y.x)*(x1.y-y.y);}
bool cmp(dian x,dian y)
{
if(pd(x,y,a[1])>eps)return true;
return false;
}
int n;
int main()
{
// freopen("sb.in","r",stdin);
n=1;
while(scanf("%lf%lf",&a[n].x,&a[n].y)!=EOF)
{
if(a[n].x<a[1].x || (fabs(a[n].x-a[1].x)<=eps && a[n].y<a[1].y)){dian z=a[n];a[n]=a[1];a[1]=z;}
n++;
}
n--;
sort(a+2,a+n+1,cmp);//类似凸包的排序
for(int i=1;i<=n;i++)
{
if(a[i].x==0 && a[i].y==0)//找到原点
{
for(int j=i;j<=n;j++)printf("(%.0lf,%.0lf)\n",a[j].x,a[j].y);
for(int j=1;j<i;j++)printf("(%.0lf,%.0lf)\n",a[j].x,a[j].y);
break;
}
}
return 0;
}
3
大概意思就是说给你一些半径一样的圆,求类似凸包的周长。
那么我们容易看出其实就是凸包的周长加上一个圆周。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
double eps=1e-8,pai=3.1415926535897932;
struct dian{double x,y;}a[1100],sta[1100];int n,len;double l;
inline double mu(dian x,dian y,dian z){return (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
inline double dis(dian x,dian y){return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));}
inline bool cmp(dian x,dian y)
{
double tt=mu(x,y,a[1]);
if(tt>eps)return true;
else if(fabs(tt)<=eps && dis(x,a[1])-dis(y,a[1])<-eps)return true;
return false;
}
int main()
{
scanf("%d%lf",&n,&l);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&a[i].x,&a[i].y);
if(a[i].x-a[1].x<-eps || (fabs(a[i].x-a[1].x)<=eps && a[i].y-a[1].y<-eps)){dian z=a[1];a[1]=a[i];a[i]=z;}
}
sort(a+2,a+n+1,cmp);
len=2;sta[1]=a[1];sta[2]=a[2];
for(int i=3;i<=n;i++)
{
while(len>1 && mu(a[i],sta[len],sta[len-1])>=-eps)len--;
sta[++len]=a[i];
}
sta[len+1]=sta[1];
double ans=0.0;
for(int i=1;i<=len;i++)ans+=dis(sta[i],sta[i+1]);
ans+=pai*2*l;//圆周
printf("%.0lf\n",ans);
return 0;
}
4
逆时针输出点,其实很弱智,但是我们可以发现不管作那个点为基准点做逆时针排序,得出的逆时针顺序都一样。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
double eps=1e-8;
struct dian{double x,y;}a[100];
double pd(dian x1,dian x2,dian y){return (x1.x-y.x)*(x2.y-y.y)-(x2.x-y.x)*(x1.y-y.y);}
bool cmp(dian x,dian y)
{
if(pd(x,y,a[1])>eps)return true;//无共线情况
return false;
}
int n;
int main()
{
n++;
while(scanf("%lf%lf",&a[n].x,&a[n].y)!=EOF)
{
if(fabs(a[n].x)<=eps && fabs(a[n].y)<=eps){dian z=a[n];a[n]=a[1];a[1]=z;}
n++;
}
n--;
sort(a+2,a+n+1,cmp);
for(int i=1;i<=n;i++)printf("(%.0lf,%.0lf)\n",a[i].x,a[i].y);
return 0;
}
5
判断是否是稳定凸包。
也就是每条边都至少有三个点。(特判直线情况)
用类似凸包的方法排序(虽然不是很需要,可以把凸包去掉,并改改代码,但是懒得改了,当时),但是应当特判最后一条边。
一个数据:
1
7
0 0
1 0
2 0
2 1
2 2
1 2
0 2
NO
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct dian
{
double x,y;
}a[1100],sta[1100];int n;double eps=1e-7;
double mu(dian x,dian y,dian z){return (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
double dis(dian x,dian y){return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));}
bool cmp(dian x,dian y)
{
double tt=mu(x,y,a[1]);
if(tt>eps)return true;
else if(fabs(tt)<=eps && dis(a[1],x)-dis(a[1],y)<=eps)return true;
return false;
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&a[i].x,&a[i].y);
if(a[i].x-a[1].x<-eps || (fabs(a[i].x-a[1].x)<=eps && a[i].y-a[1].y<-eps)){dian z=a[1];a[1]=a[i];a[i]=z;}
}
sort(a+2,a+n+1,cmp);
int top=2;sta[1]=a[1];sta[2]=a[2];
for(int i=3;i<=n;i++)
{
while(top>1 && mu(a[i],sta[top],sta[top-1])>eps)top--;
sta[++top]=a[i];
}
//凸包判排序
if((n-1)>1 && fabs(mu(a[n-1],sta[top],sta[1]))<=eps)//最后一条边有点与top共线
{
dian now=sta[top];
for(int i=top;i>1;i--)
{
if(fabs(mu(sta[i],now,sta[1]))<=eps)top--;
else break;
}//去掉最后一条边,重复点的情况。
sta[++top]=now;//保留一个点。
if(top<=2)//一条直线
{
printf("NO\n");
continue;
}
bool bk=false;
for(int i=3;i<=top;i++)
{
if(fabs(mu(sta[i-2],sta[i-1],sta[i]))<=eps)bk=true;
else
{
if(bk==true)bk=false;
else break;
}
}
if(bk==false)printf("NO\n");
else printf("YES\n");
}
else printf("NO\n");
}
return 0;
}
6
判断情况。
分别用斜率与两点式(\(ax+by+c=0\),求的公式在代码中有)做了。
斜率版:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
struct dian
{
double x,y;
dian(double xx=0.0,double yy=0.0){x=xx;y=yy;}
}xx1,yy1,xx2,yy2;double eps=1e-7;
dian pd(dian x,dian y)
{
dian z;z.x=(x.y-y.y)/(x.x-y.x);//斜率
z.y=x.y-x.x*z.x;
return z;
}
double mu(dian x,dian y,dian z){return (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
dian jd()
{
double t1=mu(yy2,xx1,xx2),t2=mu(yy2,yy1,xx2);
return dian((t1*yy1.x-t2*xx1.x)/(t1-t2),(t1*yy1.y-t2*xx1.y)/(t1-t2));
}
void solve()
{
dian q=pd(xx1,yy1),p=pd(xx2,yy2);
if(xx1.x==yy1.x)//特判y=0的直线
{
if(xx2.x==yy2.x)
{
if(xx1.x==xx2.x)printf("LINE\n");
else printf("NONE\n");
}
else printf("POINT %.2lf %.2lf\n",xx1.x,xx1.x*p.x+p.y);
return ;
}
if(fabs(q.x-p.x)<=eps)
{
if(fabs(q.y-p.y)<=eps)printf("LINE\n");
else printf("NONE\n");
}
else
{
dian z=jd();
printf("POINT %.2lf %.2lf\n",z.x,z.y);
}
}
int main()
{
// freopen("sb.out","w",stdout);
printf("INTERSECTING LINES OUTPUT\n");
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&xx1.x,&xx1.y,&yy1.x,&yy1.y,&xx2.x,&xx2.y,&yy2.x,&yy2.y);
solve();
}
printf("END OF OUTPUT\n");
return 0;
}
两点式:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
struct dian
{
double x,y;
dian(double xx=0.0,double yy=0.0){x=xx;y=yy;}
}xx1,yy1,xx2,yy2;double eps=1e-6;
struct node{double x,y,z;};
node pd(dian x,dian y)//求两点式
{
//(x-x1)/(x2-x1)=(y-y1)/(y2-y1)相似三角形可以求出此式子
//(x-x1)(y2-y1)=(y-y1)(x2-x1)相似三角形
//(y2-y1)x+(x1-x2)y+(x1y1-x1y2+y1x2-x1y1)
//(y2-y1)x+(x1-x2)y+(y1x2-x1y2)
node z;z.x=(y.y-x.y);z.y=(x.x-y.x);z.z=(x.y*y.x-x.x*y.y);
return z;
}
double mu(dian x,dian y,dian z){return (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
dian jd()
{
double t1=mu(yy2,xx1,xx2),t2=mu(yy2,yy1,xx2);
return dian((t1*yy1.x-t2*xx1.x)/(t1-t2),(t1*yy1.y-t2*xx1.y)/(t1-t2));
}
void solve()
{
node q=pd(xx1,yy1),p=pd(xx2,yy2);
if(fabs(q.x*p.y-q.y*p.x)<=eps)
{
if(fabs(q.z*p.y-p.z*q.y)<=eps && fabs(q.z*p.x-p.z*q.x)<=eps)printf("LINE\n");
else printf("NONE\n");
}
else
{
dian z=jd();
printf("POINT %.2lf %.2lf\n",z.x,z.y);
}
}
int main()
{
// freopen("sb.out","w",stdout);
printf("INTERSECTING LINES OUTPUT\n");
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&xx1.x,&xx1.y,&yy1.x,&yy1.y,&xx2.x,&xx2.y,&yy2.x,&yy2.y);
solve();
}
printf("END OF OUTPUT\n");
return 0;
}
7
//双倍经验
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
double eps=1e-8;
struct dian{double x,y;};
struct line{dian x,y;}a[110000];
double mu(dian x,dian y,dian z){return (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
double mymin(double x,double y){return x<y?x:y;}
double mymax(double x,double y){return x>y?x:y;}
bool pd(line x,dian y){return (y.x>=mymin(x.x.x,x.y.x) && y.x<=mymax(x.x.x,x.y.x) && y.y>=mymin(x.x.y,x.y.y) && y.y<=mymax(x.x.y,x.y.y));}
bool solve(line x,line y)
{
if(mu(y.y,x.x,y.x)*mu(y.y,x.y,y.x)<-eps && mu(x.y,y.x,x.x)*mu(x.y,y.y,x.x)<-eps)return true;
if((fabs(mu(x.x,y.x,y.y))<=eps && pd(y,x.x)==true) ||
(fabs(mu(x.y,y.x,y.y))<=eps && pd(y,x.y)==true) ||
(fabs(mu(y.x,x.x,x.y))<=eps && pd(x,y.x)==true) ||
(fabs(mu(y.y,x.x,x.y))<=eps && pd(x,y.y)==true))return true;
return false;
}
int n,ans[110000];
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0)break;
for(int i=1;i<=n;i++)scanf("%lf%lf%lf%lf",&a[i].x.x,&a[i].x.y,&a[i].y.x,&a[i].y.y);
ans[0]=0;
for(int i=1;i<=n;i++)
{
bool bk=false;
for(int j=i+1;j<=n;j++)
{
if(solve(a[i],a[j])==true){bk=true;break;}
}
if(bk==false)ans[++ans[0]]=i;
}
printf("Top sticks:");
for(int i=1;i<ans[0];i++)printf(" %d,",ans[i]);
printf(" %d.\n",ans[ans[0]]);
}
return 0;
}
8
逆时针的半平面交双倍经验。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define N 110
using namespace std;
double eps=1e-8;
int n;
struct dian
{
double x,y;
dian(double xx=0,double yy=0){x=xx;y=yy;}
};
struct line
{
dian x,y;
double agr;
void made(){agr=atan2(y.y-x.y,y.x-x.x);}
}a[N],sta[N];int head,tail;
inline double mu(dian x,dian y,dian z){return (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
inline dian jd(line x,line y)
{
double t1=mu(y.x,x.x,y.y),t2=mu(y.x,x.y,y.y);
return dian((t1*x.y.x-t2*x.x.x)/(t1-t2),(t1*x.y.y-t2*x.x.y)/(t1-t2));
}
inline bool safe(line x,dian y){return mu(y,x.y,x.x)<=eps;}
inline bool cmp(line x,line y)
{
if(x.agr-y.agr<-eps)return true;
else if(fabs(x.agr-y.agr)<=eps && safe(y,x.x)==true)return true;
return false;
}
inline bool bpm()
{
sort(a+1,a+n+1,cmp);
int tp=1;for(int i=2;i<=n;i++)if(fabs(a[i].agr-a[i-1].agr)>eps)a[++tp]=a[i];
n=tp;
head=1;tail=2;sta[1]=a[1];sta[2]=a[2];
for(int i=3;i<=n;i++)
{
while(head<tail && safe(a[i],jd(sta[tail],sta[tail-1]))==false)tail--;
while(head<tail && safe(a[i],jd(sta[head],sta[head+1]))==false)head++;
sta[++tail]=a[i];
}
while(head<tail && safe(sta[head],jd(sta[tail],sta[tail-1]))==false)tail--;
if(tail-head+1<=2)return false;
return true;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0)break;
for(int i=1;i<=n;i++)
{
double x,y;scanf("%lf%lf",&x,&y);
a[i-1].y.x=a[i].x.x=x;a[i-1].y.y=a[i].x.y=y;
a[i-1].made();
}
a[n].y.x=a[1].x.x;a[n].y.y=a[1].x.y;a[n].made();
if(bpm())printf("1\n");
else printf("0\n");
}
return 0;
}
9
双倍经验,变成顺时针。
//非凸多边形不能用叉积判顺逆
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define N 210
using namespace std;
double eps=1e-8;
int n;
struct dian
{
double x,y;
dian(double xx=0,double yy=0){x=xx;y=yy;}
};
struct line
{
dian x,y;
double agr;
void made(){agr=atan2(y.y-x.y,y.x-x.x);}
}a[N],sta[N];int head,tail;
inline double mu(dian x,dian y,dian z){return (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
inline dian jd(line x,line y)
{
double t1=mu(y.x,x.x,y.y),t2=mu(y.x,x.y,y.y);
return dian((t1*x.y.x-t2*x.x.x)/(t1-t2),(t1*x.y.y-t2*x.x.y)/(t1-t2));
}
inline bool safe(line x,dian y){return mu(y,x.y,x.x)<=eps;}
inline bool cmp(line x,line y)
{
if(x.agr-y.agr<-eps)return true;
else if(fabs(x.agr-y.agr)<=eps && safe(y,x.x)==true)return true;
return false;
}
inline bool bpm()
{
sort(a+1,a+n+1,cmp);
int tp=1;for(int i=2;i<=n;i++)if(fabs(a[i].agr-a[i-1].agr)>eps)a[++tp]=a[i];
n=tp;
head=1;tail=2;sta[1]=a[1];sta[2]=a[2];
for(int i=3;i<=n;i++)
{
while(head<tail && safe(a[i],jd(sta[tail],sta[tail-1]))==false)tail--;
while(head<tail && safe(a[i],jd(sta[head],sta[head+1]))==false)head++;
sta[++tail]=a[i];
}
while(head<tail && safe(sta[head],jd(sta[tail],sta[tail-1]))==false)tail--;
if(tail-head+1<=2)return false;
return true;
}
int main()
{
int T;scanf("%d",&T);
for(int i=1;i<=T;i++)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].x.x,&a[i].x.y);
for(int i=n-1;i>=1;i--)a[i+1].y=a[i].x,a[i+1].made();
a[1].y=a[n].x;a[1].made();
if(bpm())printf("YES\n");
else printf("NO\n");
}
return 0;
}
10
多次双倍经验,顺时针。
//非凸多边形不能用叉积判顺逆
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define N 210
using namespace std;
double eps=1e-8;
int n;
struct dian
{
double x,y;
dian(double xx=0,double yy=0){x=xx;y=yy;}
};
struct line
{
dian x,y;
double agr;
void made(){agr=atan2(y.y-x.y,y.x-x.x);}
}a[N],sta[N];int head,tail;
inline double mu(dian x,dian y,dian z){return (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
inline dian jd(line x,line y)
{
double t1=mu(y.x,x.x,y.y),t2=mu(y.x,x.y,y.y);
return dian((t1*x.y.x-t2*x.x.x)/(t1-t2),(t1*x.y.y-t2*x.x.y)/(t1-t2));
}
inline bool safe(line x,dian y){return mu(y,x.y,x.x)<=eps;}
inline bool cmp(line x,line y)
{
if(x.agr-y.agr<-eps)return true;
else if(fabs(x.agr-y.agr)<=eps && safe(y,x.x)==true)return true;
return false;
}
inline bool bpm()
{
sort(a+1,a+n+1,cmp);
int tp=1;for(int i=2;i<=n;i++)if(fabs(a[i].agr-a[i-1].agr)>eps)a[++tp]=a[i];
n=tp;
head=1;tail=2;sta[1]=a[1];sta[2]=a[2];
for(int i=3;i<=n;i++)
{
while(head<tail && safe(a[i],jd(sta[tail],sta[tail-1]))==false)tail--;
while(head<tail && safe(a[i],jd(sta[head],sta[head+1]))==false)head++;
sta[++tail]=a[i];
}
while(head<tail && safe(sta[head],jd(sta[tail],sta[tail-1]))==false)tail--;
if(tail-head+1<=2)return false;
return true;
}
int main()
{
int T=0;
while(scanf("%d",&n)!=EOF)
{
if(n==0)break;
T++;printf("Floor #%d\n",T);
for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].x.x,&a[i].x.y);
for(int i=n-1;i>=1;i--)a[i+1].y=a[i].x,a[i+1].made();
a[1].y=a[n].x;a[1].made();
if(bpm())printf("Surveillance is possible.\n\n");
else printf("Surveillance is impossible.\n\n");
}
return 0;
}
11
顺时针,在多求个面积。。。
//非凸多边形不能用叉积判顺逆
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define N 2110
using namespace std;
double eps=1e-8;
int n;
struct dian
{
double x,y;
dian(double xx=0,double yy=0){x=xx;y=yy;}
}pl[N];
struct line
{
dian x,y;
double agr;
void made(){agr=atan2(y.y-x.y,y.x-x.x);}
}a[N],sta[N];int head,tail;
inline double mu(dian x,dian y,dian z){return (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
inline dian jd(line x,line y)
{
double t1=mu(y.x,x.x,y.y),t2=mu(y.x,x.y,y.y);
return dian((t1*x.y.x-t2*x.x.x)/(t1-t2),(t1*x.y.y-t2*x.x.y)/(t1-t2));
}
inline bool safe(line x,dian y){return mu(y,x.y,x.x)<=eps;}
inline bool cmp(line x,line y)
{
if(x.agr-y.agr<-eps)return true;
else if(fabs(x.agr-y.agr)<=eps && safe(y,x.x)==true)return true;
return false;
}
inline void bpm()
{
sort(a+1,a+n+1,cmp);
int tp=1;for(int i=2;i<=n;i++)if(fabs(a[i].agr-a[i-1].agr)>eps)a[++tp]=a[i];
n=tp;
head=1;tail=2;sta[1]=a[1];sta[2]=a[2];
for(int i=3;i<=n;i++)
{
while(head<tail && safe(a[i],jd(sta[tail],sta[tail-1]))==false)tail--;
while(head<tail && safe(a[i],jd(sta[head],sta[head+1]))==false)head++;
sta[++tail]=a[i];
}
while(head<tail && safe(sta[head],jd(sta[tail],sta[tail-1]))==false)tail--;
if(tail-head+1<=2)
{
printf("0.00\n");
return ;
}
double ans=0;int len=0;
for(int i=head;i<tail;i++)pl[++len]=jd(sta[i],sta[i+1]);pl[++len]=jd(sta[head],sta[tail]);
for(int i=3;i<=len;i++)ans+=mu(pl[i-1],pl[i],pl[1]);
printf("%.2lf\n",fabs(ans/2));
}
int main()
{
int T;scanf("%d",&T);
for(int i=1;i<=T;i++)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].x.x,&a[i].x.y);
for(int i=n-1;i>=1;i--)a[i+1].y=a[i].x,a[i+1].made();
a[1].y=a[n].x;a[1].made();
bpm();
}
return 0;
}
社区送福利
点到直线的距离
这里放上一个大佬的另类证法,很强!
膜拜大佬。
如何将直线移动1个单位的距离?
这里指的是垂直距离,那么就不是+1-1的事情了,这里用两点式来进行证明其中的一种情况(其他情况也可以证)
现在有一条直线:\(ax+by=0(a<0,b>0)\)
\(AC⊥A\)所在的直线
\(AC=1\),设\(C\)点的\(x\)坐标为\(k\),那么我们可以求出\(AC\)的两点时\(bx-ay=0\),那么进而求出\(AD=-\frac{bx}{a}\),勾股得出\(AC=\frac{x\sqrt{a^{2}+b^{2}}}{-a}=1\),那么\(x=\frac{-a\sqrt{a^{2}+b^{2}}}{a^{2}+b^{2}}\),那么\(C(\frac{-a\sqrt{a^{2}+b^{2}}}{a^{2}+b^{2}},\frac{-b\sqrt{a^{2}+b^{2}}}{a^{2}+b^{2}})\),然后我们通过将\(x,y\)带入\(ax+by+?\)求出\(?\)是\(\sqrt{a^{2}+b^{2}}\),那么我们就知道移动1就是加减\(\sqrt{a^{2}+b^{2}}\)。