Surround the Trees ssl 1720 计算几何

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.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值