Description
There are a lot of trees in an area. A peasant wants to buy a rope to surround all these trees. So at first he must know the minimal required length of the rope. However, he does not know how to calculate it. Can you help him?
The diameter and length of the trees are omitted, which means a tree can be seen as a point. The thickness of the rope is also omitted which means a rope can be seen as a line.
There are no more than 100 trees.
Input
The input contains one or more data sets. At first line of each input data set is number of trees in this data set, it is followed by series of coordinates of the trees. Each coordinate is a positive integer pair, and each integer is less than 32767. Each pair is separated by blank.
Zero at line for number of trees terminates the input for your program.
Output
The minimal length of the rope. The precision should be 10^-2.
分析
用最暴力的Jarvis 算法求凸包
Jarvis 算法如下:
我们无法在计算机上实现直线的旋转,因而不能直接套用数学构造法,但是可以通过对点的扫描达到同样的目的。其实,绕着A 旋转l 找到的是与l 夹角最小的一条边,也是必然在凸包上的边
以上图为例,
轴点为A,l 向顺时针方向旋转,目标点放在点变量P 中。P 的初始值可以是除A 以外任意一点。
然后对每一个点Pi,根据矢量的叉积和定向旋转,如果(AP× i AP )z>0,即AP转到i AP 为逆时针, i AP 与l 的夹角小于AP 与l 的夹角;
或者((AP× i AP )z=0)and(|APi| >|AP|),即A,P,Pi三点共线,Pi 离A 点更远,则P:=Pi。
这样,找到一个逆时针方向的Δφ,AP就转过去。把所有的点都扫描一遍之后,AP 就转到了与l 夹角最小的边上,也就是凸包上的一条边了,该过程记为PROC1。
Jarvis 算法的核心部分就是上面的PROC1,主要流程如下:
V0 := P1~Pn 中与某一点距离最大的点 {必然在凸包上的点}
Repeat
PROC1
Until 目标点变量P=V0。{重复次数等于凸包上点的个数}
代码
var
n:longint;
x,y:array[1..200] of longint;
x0,y0:longint;
ans:real;
procedure init;
var
i,j,k:longint;
begin
for i:=1 to n do
begin
readln(x[i],y[i]);
if (y[i]<y0) or ((y[i]=y0) and (x[i]<x0))
then begin
y0:=y[i];
x0:=x[i];
end;
end;
end;
procedure main;
var
dis:real;
flag:boolean;
x1,y1:longint;
x2,y2:longint;
i,j,k:longint;
begin
x1:=x0;
y1:=y0;
ans:=0;
repeat
dis:=0; j:=0;
x2:=0; y2:=0;
flag:=true;
for i:=1 to n do
begin
if (x[i]=x1) and (y[i]=y1)
then continue;
if flag
then
begin
flag:=false;
x2:=x[i]; y2:=y[i];
dis:=sqrt(sqr(x2-x1)+sqr(y2-y1));
end;
if (x2-x1)*(y[i]-y1)-(y2-y1)*(x[i]-x1)<0
then begin
x2:=x[i]; y2:=y[i];
dis:=sqrt(sqr(x2-x1)+sqr(y2-y1));
end;
if (x2-x1)*(y[i]-y1)-(y2-y1)*(x[i]-x1)=0
then
if dis<sqrt(sqr(x[i]-x1)+sqr(y[i]-y1)) then
begin
x2:=x[i]; y2:=y[i];
dis:=sqrt(sqr(x2-x1)+sqr(y2-y1));
end;
end;
ans:=ans+dis;
x1:=x2;
y1:=y2;
until (x1=x0) and (y1=y0);
end;
begin
readln(n);
while n<>0 do
begin
x0:=maxlongint;
y0:=maxlongint;
init;
main;
if n=1
then ans:=0;
writeln(ans:0:2);
readln(n);
end;
end.
分析2
方法2
算法如下(Graham算法):
1)求q中y坐标最小的点p0,若具有最小坐标的点有多个,则取最左边的点作为po.
2)对q中剩余的点按逆时针相对p0的极角排序,若有数个保留其中距p0最远的点
得到序列(p1,p2,…pn-1);
3)p0,p1,p2相继入栈
4)for i=3 to n-1 do
1) while 由次栈顶元素、栈顶元素和Pi所形成角不是向左转do栈顶元素出栈s;
2)pi入栈
5)打印按逆时针排列的栈中的各顶点
要特殊处理n=1和n=2 的情况
代码
type
arr=record
x,y:longint;
end;
var
a:array[0..10000] of arr;
tot:longint;
num:longint;
stack:array[0..10000] of longint;
n:longint;
ans:real;
i,j,k:longint;
function m(p1,p2,p0:arr):real;
begin
m:=(p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
end;
procedure init;
var
i,j,k:longint;
x,y:longint;
begin
fillchar(a,sizeof(a),0);
x:=maxlongint;
y:=maxlongint;
num:=0;
for i:=1 to n do
begin
readln(a[i].x,a[i].y);
if (a[i].y<y) or ((a[i].y=y) and (a[i].x<x))
then begin
x:=a[i].x;
y:=a[i].y;
num:=i;
end;
end;
a[0].x:=x;
a[0].y:=y;
end;
function comp(p1,p2:arr):boolean;
var
t:real;
begin
t:=m(p1,p2,a[0]);
if (t>0) or (t=0) and (sqr(p1.x-a[0].x)+sqr(p1.y-a[0].y)<
sqr(p2.x-a[0].x)+sqr(p2.y-a[0].y))
then comp:=true
else comp:=false;
end;
procedure qsort(l,r:longint);
var
i,j:longint;
key:arr;
temp:arr;
begin
if l>=r then exit;
i:=l; j:=r;
key:=a[l+random(r-l+1)];
repeat
while comp(a[i],key) do inc(i);
while comp(key,a[j]) do dec(j);
if i<=j then
begin
temp:=a[i]; a[i]:=a[j]; a[j]:=temp;
inc(i);dec(j);
end;
until i>j;
qsort(l,j);
qsort(i,r);
end;
begin
while 1=1 do begin
readln(n);
if n=0 then exit;
init;
qsort(1,n);
tot:=3;
fillchar(stack,sizeof(stack),0);
stack[1]:=1;
stack[2]:=2;
stack[3]:=3;
for i:=4 to n do
begin
while (tot>1) and (m(a[i],a[stack[tot]],a[stack[tot-1]])>=0) do tot:=tot-1;
tot:=tot+1;
stack[tot]:=i;
end;
ans:=0;
if n=2 then tot:=tot-1;
for i:=2 to tot do
ans:=ans+sqrt(sqr(a[stack[i]].x-a[stack[i-1]].x)+sqr(a[stack[i]].y-a[stack[i-1]].y));
ans:=ans+sqrt(sqr(a[stack[1]].x-a[stack[tot]].x)+sqr(a[stack[1]].y-a[stack[tot]].y));
if n=1 then ans:=0;
writeln(ans:0:2);
end;
end.