前言
本来之前很早就在某czy的计算几何专题上了解了计算几何的一些小知识。
然而当时太年轻,学不懂。
到现在,突然来了热血。翻了必修四一遍又一遍,百度百科也是看了一遍又一遍。
最终有所成,但是没复习,导致现在忘了许多。
于是现在开始好好打篇博客来权当复习。
当然,也顺便学习了一下别人初一就会的东东。
向量
这个东东很简单,指具有大小和方向的量。就是一个类似于箭头的线 (不是♂)
这个东东高中讲到物理的正交分解之类的就知道是长什么样的了。
其实信息学对于这个研究大多只需要二维平面以及一些基本运算。
书写时在字母顶上加一小箭头“→”。如果给定向量的起点(A)和终点(B),可将向量记作
A
B
→
\overrightarrow {AB}
AB。
接下来就有一个比较有用的东东:模长:表示为|a|,意思是一个向量的长度。
其他似乎没有了。
接下来就讲讲这些运算。
当然,向量可以表示为一个坐标(x,y)
向量+
原谅我盗个图:
在这个图中我们清晰地看到,两个向量相加就相当于两个向量组成的平行四边形的对角线。
A
→
\overrightarrow A
A表示为(x1,y1)
B
→
\overrightarrow B
B表示为(x2,y2)
则计算表示为
A
→
+
B
→
=
C
→
\overrightarrow A+\overrightarrow B=\overrightarrow C
A+B=C
坐标表示为:(x1+x2,y1+y2)
向量-
这个其实很好理解。
我们可以知道,一个向量
A
→
\overrightarrow A
A取反即表示为
−
A
→
\overrightarrow {-A}
−A
那么就可以把它用加法来弄即可。
数乘
表示为一个向量乘上一个常数。
意思就是一个向量变长或缩短或取相反。
表示为:
A
→
∗
(
一
个
常
数
)
\overrightarrow A*(一个常数)
A∗(一个常数)
注意小心常数为0的情况。
点积
首先,点积是给出两个向量,
A
→
\overrightarrow A
A、
B
→
\overrightarrow B
B
然后求出一个定值(不是向量)
表示为
A
→
⋅
B
→
\overrightarrow A·\overrightarrow B
A⋅B
这个有两种定义:
代数定义
A
→
⋅
B
→
=
x
1
∗
x
2
+
y
1
∗
y
2
\overrightarrow A·\overrightarrow B=x1*x2+y1*y2
A⋅B=x1∗x2+y1∗y2
几何定义
看到一个图:
我们可以从中得到:
A
→
⋅
B
→
=
∣
A
→
∣
∗
∣
B
→
∣
∗
c
o
s
θ
(
夹
角
大
小
)
\overrightarrow A·\overrightarrow B=|\overrightarrow A|*|\overrightarrow B|*cos \theta(夹角大小)
A⋅B=∣A∣∗∣B∣∗cosθ(夹角大小)
也就是图中的投影*原长
至于上述两种定义如何互相转化,有很多方法证明
这里有一个我比较喜欢的做法:
首先,我们发现两个向量可以组成一个三角形。
我们设第三边是
C
→
\overrightarrow C
C
则根据余弦定理得:
∣
C
→
∣
2
=
∣
A
→
∣
2
+
∣
B
→
∣
2
−
2
∗
∣
B
→
∣
∗
∣
A
→
∣
∗
c
o
s
θ
|\overrightarrow C|^2=|\overrightarrow A|^2+|\overrightarrow B|^2-2*|\overrightarrow B|*|\overrightarrow A|*cos \theta
∣C∣2=∣A∣2+∣B∣2−2∗∣B∣∗∣A∣∗cosθ
移一下项:
∣
B
→
∣
∗
∣
A
→
∣
∗
c
o
s
θ
=
∣
A
→
∣
2
+
∣
B
→
∣
2
−
∣
C
→
∣
2
2
|\overrightarrow B|*|\overrightarrow A|*cos \theta=\frac{|\overrightarrow A|^2+|\overrightarrow B|^2-|\overrightarrow C|^2}2
∣B∣∗∣A∣∗cosθ=2∣A∣2+∣B∣2−∣C∣2
拆掉后面的向量模长,得
∣
B
→
∣
∗
∣
A
→
∣
∗
c
o
s
θ
=
x
1
2
+
y
1
2
+
x
2
2
+
y
2
2
−
(
x
1
−
x
2
)
2
−
(
y
1
−
y
2
)
2
2
|\overrightarrow B|*|\overrightarrow A|*cos \theta=\frac{x1^2+y1^2+x2^2+y2^2-(x1-x2)^2-(y1-y2)^2}2
∣B∣∗∣A∣∗cosθ=2x12+y12+x22+y22−(x1−x2)2−(y1−y2)2
于是就可以证明啦~~
当然,可以推导到3维的去。然而信息学大多只讨论2维。
叉积
这个其实就是求两个向量围成的平行四边形的有向面积。
有向面积是什么呢?其实就是在左手向为正,右手向为负。
表示:
B
→
×
A
→
或
B
→
∧
A
→
\overrightarrow B×\overrightarrow A或\overrightarrow B∧\overrightarrow A
B×A或B∧A
定义——一样有两个
几何定义
B
→
×
A
→
=
∣
B
→
∣
∗
∣
A
→
∣
∗
sin
θ
\overrightarrow B×\overrightarrow A=|\overrightarrow B|*|\overrightarrow A|*\sin \theta
B×A=∣B∣∗∣A∣∗sinθ(也就是四边形面积)
代数定义
A
→
×
B
→
=
x
1
∗
y
2
−
y
1
∗
x
2
\overrightarrow A×\overrightarrow B=x1*y2-y1*x2
A×B=x1∗y2−y1∗x2
证明?
似乎是类似于上面的。
于是这里就介绍完大部分的向量运算了。
(lj浏览器刚刚打的全部都没有保存,气死偶类)
当然,我们当然可以在中考数学里用这个向量,会有70%的几率老师看不懂而打×。
凸包
你跟我说你不会?
给你一个博客自生自灭吧
https://blog.csdn.net/enjoy_pascal/article/details/78397028
裸题:jzoj2881【SHTSC2012day1】信用卡凸包
贴个程序跑路
uses math;
type
new=record
x,y,jj,jl:extended;
kind,bh:longint;
end;
new1=record
x,y,r:extended;
end;
new2=record
a,b,c:extended;
end;
var
i,j,k,l,n,m,gs,t,num,now:longint;
cir:array[1..250000] of new1;
jd:new;
a,b,r,x,y,xx,yy:extended;
spot:array[1..250000] of new;
dl:array[1..250000] of longint;
fx:array[1..4,1..2] of longint=((-1,1),(-1,-1),(1,-1),(1,1));
dt,nx,ny,answer:extended;
tir:new2;
function dist(x1,y1,x2,y2:extended):extended;
begin
exit(sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)));
end;
function judge(ax,ay,bx,by,cx,cy:extended):extended;
begin
exit(ax*by+cx*ay+bx*cy-cx*by-bx*ay-ax*cy);
end;
procedure con_bag;
var
i,j,k,up:longint;
x,y,miny,minx,a,b,c:extended;
procedure qsort(l,r:longint);
var
i,j:longint;
m,m1:extended;
k:new;
begin
i:=l;j:=r;
m:=spot[(l+r) div 2].jj;
m1:=spot[(l+r) div 2].jl;
repeat
while (spot[i].jj<m) or ((spot[i].jj=m) and (spot[i].jl<m1)) do inc(i);
while (spot[j].jj>m) or ((spot[j].jj=m) and (spot[j].jl>m1)) do dec(j);
if i<=j then
begin
k:=spot[i];
spot[i]:=spot[j];
spot[j]:=k;
inc(i);dec(j);
end;
until i>j;
if l<j then qsort(l,j);
if r>i then qsort(i,r);
end;
begin
gs:=0;
miny:=maxlongint;
minx:=maxlongint;
for i:=1 to num do
begin
if (spot[i].y<miny) or ((spot[i].y=miny) and (spot[i].x<minx)) then
begin
miny:=spot[i].y;
minx:=spot[i].x;
j:=i;
end;
end;
x:=spot[j].x;
y:=spot[j].y;
for i:=1 to num do
begin
spot[i].x:=spot[i].x-x;
spot[i].y:=spot[i].y-y;
if spot[i].y=0 then spot[i].jj:=0
else
if spot[i].x=0 then spot[i].jj:=90
else
if spot[i].x<0 then spot[i].jj:=arctan(spot[i].y/spot[i].x)*(180/pi)+180
else spot[i].jj:=arctan(spot[i].y/spot[i].x)*(180/pi);
spot[i].jl:=dist(0,0,spot[i].x,spot[i].y);
end;
qsort(1,num);
up:=2;
dl[1]:=1;
dl[2]:=2;
for i:=3 to num do
begin
while (up>1) and (judge(spot[dl[up-1]].x,spot[dl[up-1]].y,spot[dl[up]].x,spot[dl[up]].y,spot[i].x,spot[i].y)<=0) do dec(up);
if (spot[dl[up]].x<>spot[i].x) or (spot[dl[up]].y<>spot[i].y) then
begin
inc(up);
dl[up]:=i;
end;
end;
inc(up);
dl[up]:=dl[1];
answer:=0;
for i:=2 to up do
begin
answer:=answer+dist(spot[dl[i]].x,spot[dl[i]].y,spot[dl[i-1]].x,spot[dl[i-1]].y);
end;
writeln(answer+2*pi*r:0:2);
end;
begin
readln(n);
readln(a,b,r);
a:=a/2-r;
b:=b/2-r;
m:=0;
for i:=1 to n do
begin
readln(x,y,dt);
for j:=1 to 4 do
begin
xx:=fx[j,1]*a;
yy:=fx[j,2]*b;
inc(m);
cir[m].x:=x+yy*cos(dt)-xx*sin(dt);
cir[m].y:=y+xx*cos(dt)+yy*sin(dt);
cir[m].r:=r;
inc(num);
spot[num].x:=cir[m].x;
spot[num].y:=cir[m].y;
end;
end;
n:=m;
con_bag;
end.
半平面交
这个很高大上,其实思路很简单。
首先:这个东东是什么?
我们想象现在有一张A4白纸,现在我们一刀下去分成两片纸,那么其中一片即为半平面。
那么半平面交就是很多半平面相交的部分。
似乎这个东东很像线性规划里的知识。
先不理了。
那么这个有什么用?
一个古老的问题——
给定一个任意的多边形,一个人站在里面,如果他可以看见这个多边形任意一个角落(所有边都可以看到不被阻挡),那么这个点是一个核
求核的面积。
那么我们从这个问题下手。
求这个n边形的核?
首先我们逆时针弄出n个向量(类似)
我们再按照极角排序。
然后关键一步——
假设现在是在一张白纸上。
编号从小到大来做,每次把当前向量的左手边保留,右手边删去。
第一步:
第二步:
多步以后
于是,中间白色部分的面积就是要求的面积了。
怎么得到?
我们记录下与白色面积相连的直线。
于是上面的图的直线集合即为{1,3,4,6,8}
不好求吗?
由于得到直线集合,那么相邻两个直线交点不是可以求吗?
直接用叉积求和即可。
优化?
极角排序时把相同的两个线保留左边的。
似乎优化不了多少。
但没关系,算法已经是
O
(
n
O(n
O(n
l
o
g
log
log
n
)
n)
n)的了
算法流程:
1、以逆时针为正方向,建边。(输入方向不确定时,可用叉乘求面积看正负得知输入的顺逆方向。)
2、极角排序。
3、用有两个端点的队列储存线段集合。(意思是一个首可以删,尾可以删的队列)
4、枚举判断一个直线加入后对半平面交的影响。(对队列的头部和尾部进行判断)
5、如果某条在队列中的直线已经对半平面交没有影响了,就从队列中删掉。
6、最后剩下的直线集合,即为最后要求的半平面交。
7、求面积
很简单对吧?
其实思想和凸包很像很像。
代码待更……
旋转卡壳
“旋转卡壳有十六样读法,你知道么?”
这个旋转卡壳是干什么的呢?
其实主要就是求出一个叫做对踵点的东东。
如果过凸包上的两个点可以画一对平行直线,使凸包上的所有点都夹在两条平
行线之间或落在平行线上,那么这两个点叫做一对对踵点。
其实就是下图:
两个平行红线交于凸包另个点。这两个点即为对踵点。
当然,下面这种情况也是:
四个点也是一样的,就不画了。
求出来了有什么用呢?
我们可以惊讶地发现,如果要求一个凸多边形的直径(也就是最长的边)
那么就是在对踵点里面找即可。
所以有第一个用处:
求直径
我们发现,在第一个图中,有很多的绿色线段。
那就是我们要求的东东。
Shamos (1978) 提供了一个奇妙的算法可以做到
O
(
n
)
O(n)
O(n)求出所有对应的对踵点。
于是就可以统计答案。
具体怎么做,我们分回精讲。
1、我们首先可以任意地找到两个在y(x)轴上最小以及最大的两个点,并做两条平行线(平行线不用真正地弄出来,只需要记录点即可)
2、我们发现,这两个点已经是一个对踵点了。于是我们考虑旋转这两条平行线,(也不用真正地旋转)顺时针或逆时针都可。
3、旋转直到一条线碰到了凸多边形任意一点。如下图:
此时我们就可以认为找到了新的一个对踵点。(这里可以用叉积大法或直接判断距离是否缩小)
于是更新答案。
然后一直重复旋转直到统计完一圈。于是我们就可以发现,对于每个对踵点都统计到了。
所以时间是
O
(
n
)
O(n)
O(n)的。
代码?
这是POJ2187 Beauty Contest的代码(至于poj一直ce我就不知道为什么了,但是落谷可过)
uses math;
type
nod=record
x,y:int64;
end;
dot=record
x,y:int64;
end;
var
i,j,k,l,n,m,zx,zd,jlx,jld,nx,nd,x,y,rak,row,tot,up,cs:longint;
poit,f:array[0..200000] of nod;
ange:array[0..200000]of
record
x,y,value,dis:extended;
end;
st:array[0..200000]of longint;
ans,op:int64;
function judge(ax,ay,bx,by,cx,cy:extended):extended;
begin
exit(ax*by+cx*ay+bx*cy-cx*by-bx*ay-ax*cy);
end;
function arccot(xx,yy:extended):extended;
begin
if yy=0 then exit(0);
if xx=0 then exit(90);
arccot:=arctan(yy/xx)*180/pi;
if xx<0 then arccot:=arccot+180;
end;
procedure qsort(l,r:longint);
var
i,j:longint;
mid,mid1:extended;
begin
i:=l;
j:=r;
mid:=ange[(l+r)shr 1].value;
mid1:=ange[(l+r)shr 1].dis;
repeat
while (ange[i].value<mid)or(ange[i].value=mid)and(ange[i].dis<mid1)do inc(i);
while (ange[j].value>mid)or(ange[j].value=mid)and(ange[j].dis>mid1)do dec(j);
if i<=j then
begin
ange[0]:=ange[i];
ange[i]:=ange[j];
ange[j]:=ange[0];
inc(i);
dec(j);
end;
until i>j;
if l<j then qsort(l,j);
if i<r then qsort(i,r);
end;
function diss(x,y:longint):int64;
begin
exit({sqrt(}sqr(poit[x].x-poit[y].x)+sqr(poit[x].y-poit[y].y){)});
end;
function cos(a,b:dot):int64;
begin
exit(a.x*b.y-a.y*b.x);
end;
function gc(x,y,z:longint):int64;
var
a,b:dot;
begin
a.x:=poit[x].x-poit[z].x;
a.y:=poit[x].y-poit[z].y;
b.x:=poit[y].x-poit[z].x;
b.y:=poit[y].y-poit[z].y;
exit(cos(a,b));
end;
begin
//assign(input,'diameter.in');reset(input);
readln(n);
rak:=maxlongint;
j:=0;
for i:=1 to n do
with f[i] do
begin
readln(x,y);
if y<rak then
begin
row:=x;
rak:=y;
end;
if i>1 then
begin
if f[i].y<>f[i-1].y then
begin
j:=1;
end;
if f[i].x<>f[i-1].x then
begin
k:=2;
end;
end;
end;
if j=0 then
begin
zx:=f[1].x;zd:=f[1].x;
for i:=2 to n do
begin
if f[i].x<zx then zx:=f[i].x;
if f[i].x>zd then zd:=f[i].x;
end;
writeln(sqr(zd-zx));
halt;
end;
if k=0 then
begin
zx:=f[1].y;zd:=f[1].y;
for i:=2 to n do
begin
if f[i].y<zx then zx:=f[i].y;
if f[i].y>zd then zd:=f[i].y;
end;
writeln(sqr(zd-zx));
halt;
end;
for i:=1 to n do
with f[i] do
begin
x:=x-row;
y:=y-rak;
end;
tot:=1;
for i:=1 to n do
with f[i] do
if (x<>0)or(y<>0) then
begin
inc(tot);
ange[tot].value:=arccot(x,y);
ange[tot].x:=x;
ange[tot].y:=y;
ange[tot].dis:=sqrt(x*x+y*y);
end;
qsort(2,n);
up:=2;
st[1]:=1;
st[2]:=2;
for i:=3 to n do
begin
while judge(ange[st[up-1]].x,ange[st[up-1]].y,ange[st[up]].x,ange[st[up]].y,ange[i].x,ange[i].y)<0 do dec(up);
inc(up);
st[up]:=i;
end;
n:=up;
for i:=1 to n do
begin
poit[i].x:=trunc(ange[st[i]].x);
poit[i].y:=trunc(ange[st[i]].y);
end;
zx:=maxlongint;
zd:=-maxlongint;
for i:=1 to n do
begin
if poit[i].y<zx then
begin
zx:=poit[i].y;
jlx:=i;
end;
if poit[i].y>zd then
begin
zd:=poit[i].y;
jld:=i;
end;
end;
i:=jlx;
k:=jld;
ans:=diss(i,k);
for l:=1 to n do
begin
i:=i mod n+1;
cs:=0;
if k=i then
k:=i mod n+1;
j:=gc(k,i mod n+1,i);
while true do
begin
x:=gc(k mod n+1,i mod n+1,i);
if x<j then
begin
j:=x;
if diss(k mod n+1,i)>ans then ans:=diss(k mod n+1,i);
if diss(k,i)>ans then ans:=diss(k,i);
k:=k mod n+1;
end
else
break;
inc(cs);
if cs>n+1 then
begin
break;
end;
end;
end;
writeln(ans);
end.
第二个用处——
求宽
看下图:
实际上就是求中间绿绿的线的最长长度。
怎么做?
我们一样旋转卡壳。
只是每次旋转旋转到了一个新的边时,答案利用点到直线距离公式求即可。
至于点到直线距离公式怎么求,其实很简单——
第一种方法
由于我们知道3个点的坐标,直接海伦公式,然后除以底边即可。
第二种方法
其实只知道一个坐标和一个直线即可求。
利用向量,求出叉积(当然点积也可,只是很麻烦)除以直线的模长即可。
代码其实差不多。不写了
第三个用处——
凸边形间最长距离
如图:
其实问题就是两个凸多边形间,一个点在凸边形1号,另一个点在凸边形2号时,两个点间最长距离。
一个想法?
直接把两个凸多边形给合并,把点标号一下,然后同上面做法。
然而可能会特别麻烦,下面有一个特别好用,且简单的方法——
首先我们先把直线给定个方向——
然后我们钦定左边的直线只能绕着左边的凸多边形旋转,右边的直线只能绕着右边的凸多边形旋转。
并且,我们可以发现,凸多边形始终在属于自己直线的右边。
于是再旋转过程中,就会出现这种情况——
好理解吧?
实现方法——
1、我们首先可以任意地找到两个在y轴上最小以及最大的两个点,并做两条平行线
2、我们考虑旋转这两条平行线,顺时针。
3、旋转直到某一条线碰到了属于自己的凸多边形的任意一点。说明找到了对踵点。
于是更新答案。
虽然要多存放一个凸多边形。
但是这个可以做更棒的东东——
第四个用处——
凸边形间最短距离
其实做法与上面的一模一样,只是统计答案时改动一下即可。
(由于篇幅问题,接下来的什么三角剖分什么凸包交什么最小矩阵覆盖之类的,就不在这篇里写了。)
快速排斥实验以及跨立实验。
这个东东其实是判断两条线段有没有相交。
它分为两步走——
第一步快速排斥试验
首先,我们发现两条线段可以作为一个矩阵的对角线,那么我们先判断两个矩阵有无相交。
我们就有4个点,我们依照这4个点来判断——
设(p1p2)是第一条线段,(p3p4)是第二条线段。
则同时满足下面所有的式子就表示相交——
(
m
a
x
(
x
1
,
x
2
)
>
=
m
i
n
(
x
3
,
x
4
)
)
(max(x1,x2)>=min(x3,x4))
(max(x1,x2)>=min(x3,x4))
(
m
a
x
(
x
3
,
x
4
)
>
=
m
i
n
(
x
1
,
x
2
)
)
(max(x3,x4)>=min(x1,x2))
(max(x3,x4)>=min(x1,x2))
(
m
a
x
(
y
1
,
y
2
)
>
=
m
i
n
(
y
3
,
y
4
)
)
(max(y1,y2)>=min(y3,y4))
(max(y1,y2)>=min(y3,y4))
(
m
a
x
(
y
3
,
y
4
)
>
=
m
i
n
(
y
1
,
y
2
)
)
(max(y3,y4)>=min(y1,y2))
(max(y3,y4)>=min(y1,y2))
此时,我们如果发现两个矩阵相交了,我们就做第二步
跨立实验
这时我们要测试两个线段是否有相交。
那么我们发现,两条线段相交的情况当且仅当其中一条线段两个端点在另一条线段所在直线的两端。
判断两点在不在一条直线两边其实很好判断,由于有四个点,那么我们可以利用叉积。
如果
p
4
p
1
→
×
p
4
p
3
→
\overrightarrow {p4p1}×\overrightarrow {p4p3}
p4p1×p4p3的符号与
p
4
p
2
→
×
p
4
p
3
→
\overrightarrow {p4p2}×\overrightarrow {p4p3}
p4p2×p4p3的符号一致,证明p1和p2都在p4p3这条直线的一侧,证明没有相交。反之则有相交。
再判断一下
p
2
p
4
→
×
p
2
p
1
→
\overrightarrow {p2p4}×\overrightarrow {p2p1}
p2p4×p2p1的符号与
p
2
p
3
→
×
p
2
p
1
→
\overrightarrow {p2p3}×\overrightarrow {p2p1}
p2p3×p2p1是否一致,然后就可以判断了。
当然,要注意叉积等于0的情况——
和
这样就ok啦~
裸题jzoj3856 【NOIP2014八校联考第3场第1试10.4】规避
type
new=record
x,y:int64;
end;
var
i,j,k,l,n,m,t,gs:longint;
map,f:array[1..300,1..300] of real;
edge,point:array[1..1300] of new;
id:array[1..300] of longint;
maxx:real;
bz:boolean;
function max(x,y:real):real;
begin
if x>y then exit(x);
exit(y);
end;
function min(x,y:real):real;
begin
if x>y then exit(y);
exit(x);
end;
function dist(a,b:new):real;
begin
exit(sqrt(sqr(a.x-b.x)+sqr(a.y-b.y)));
end;
function jian(a,b:new):new;
begin
jian.x:=a.x-b.x;
jian.y:=a.y-b.y;
end;
function cheng(a,b:new):real;
begin
exit(a.x*b.y-a.y*b.x);
end;
function cross(p1,p2,p3,p4:new):boolean;
var
p5,p6,p7,p8,p9,p10:new;
begin
if (max(p1.x,p2.x)>=min(p3.x,p4.x)) and (max(p3.x,p4.x)>=min(p1.x,p2.x))
and(max(p1.y,p2.y)>=min(p3.y,p4.y)) and (max(p3.y,p4.y)>=min(p1.y,p2.y)) then
begin
p5:=jian(p4,p1);
p6:=jian(p3,p1);
p7:=jian(p2,p1);
p8:=jian(p4,p3);
p9:=jian(p2,p3);
p10:=jian(p1,p3);
if (cheng(p6,p7)*cheng(p5,p7)<0) and
(cheng(p10,p8)*cheng(p9,p8)<0) then
begin
exit(true);
end;
exit(false);
end
else
exit(false);
end;
begin
//assign(input,'0data.in');reset(input);
for i:=1 to 300 do for j:=1 to 300 do map[i,j]:=265536;
maxx:=265536;
readln(t);
j:=0;
while t>0 do
begin
dec(t);
readln(n);
for i:=1 to n do
begin
readln(point[i+j].x,point[i+j].y);
id[i+j]:=t+1;
if i>1 then
begin
inc(gs);
edge[gs].x:=i+j;
edge[gs].y:=i+j-1;
map[i+j-1,i+j]:=dist(point[i+j],point[i+j-1]);
map[i+j,i+j-1]:=map[i+j-1,i+j];
if i>2 then
begin
inc(gs);
edge[gs].x:=i+j;
edge[gs].y:=i+j-2;
end;
end;
end;
inc(gs);
edge[gs].x:=n+j;
edge[gs].y:=1+j;
map[1+j,n+j]:=dist(point[1+j],point[n+j]);
map[n+j,1+j]:=map[1+j,n+j];
j:=j+n;
end;
m:=j;
for i:=1 to m do
begin
for j:=1 to m do
begin
if (i<>j) and (id[i]<>id[j]) then
begin
bz:=true;
for k:=1 to gs do
begin
if cross(point[i],point[j],point[edge[k].x],point[edge[k].y]) then
begin
bz:=false;
break;
end;
end;
if bz then
begin
map[i,j]:=dist(point[i],point[j]);
map[j,i]:=map[i,j];
end;
end;
end;
end;
f:=map;
for k:=1 to m do
begin
for i:=1 to m do
begin
for j:=1 to m do
begin
if f[i,j]>f[i,k]+f[k,j] then
f[i,j]:=f[i,k]+f[k,j];
end;
end;
end;
readln(i,j);
writeln(f[i,j]:0:4);
end.
各种三角函数
待更……