Описание
На плоскости построили N окружностей

Задание
Определите площадь пересечения имеющихся окружностей


Входные данные
В первой строке входного файла находится целое число N (1
N20). В каждой из последующих N строк записана тройка вещественных чисел, описывающих очередную из окружностей. Первые два числа задают координаты ее центра, третье - радиус

Выходные данные
Выведите в выходной файл искомую площадь не менее чем с шестью верными значащими цифрами


Например:

AREA.IN
2
0 0 1
1 1 1

AREA
.OUT
0.570796






Идеи
Пересечение окружностей, полярный угол, площадь многоугольника и сектора

Комментарии

Пересечением нескольких окружностей является выпуклая фигура, которую можно представить как многоугольник и сегменты, примыкающие к его сторонам. Это разбиение на многоугольник и сегменты не является единственным, поскольку любой сегмент можно разбить на треугольник и два новых сегмента (см. рисунок ниже)

Приступим к решению. Вначале найдем точки пересечения всех пар окружностей. Если точка пересечения двух окружностей принадлежит всем остальным, то она является вершиной интересующего нас многоугольника. Получив все такие вершины, упорядочим их сортировкой по полярному углу относительно центра масс

По построенному многоугольнику, "вписанному" в пересечение, в некоторых случаях уже можно посчитать искомую площадь. Действительно, каждая его сторона является хордой некоторой окружности (обратитесь к рисунку). Сложив суммарную площадь всех соответствующих сегментов этих окружностей с площадью многоугольника, получим ответ. Однако в других случаях при этом возникнут проблемы. Во-первых, точек, являющихся его вершинами, может не оказаться вовсе, либо найдется только одна его вершина, а пересечение при этом будет непустым. (Рассмотрите случаи, когда одна окружность вложена в другую или они касаются внутренним образом). Во-вторых, в случае, например, пересечения только двух окружностей многоугольник вырождается в отрезок, являющийся хордой сразу обеих этих окружностей. При попытке подсчета площади прилегающего к нему сегмента не совсем ясно, какой из сегментов и какой из окружностей следует выбрать

Для решения всех перечисленных проблем предлагается выполнить следующее построение. Для каждой пары окружностей найдем точки пересечения прямой, проходящей через их центры, с самими окружностями (если мы встретим две концентрические окружности, то внешнюю из них нужно отбросить). Из этих точек выберем только те, которые принадлежат всем окружностям, и добавим их к списку вершин нашего многоугольника. В результате добавления этих вершин для любой стороны многоугольника (возможно вырожденного) сегмент, прилегающий к ней, будет определяться однозначно, так как соседние вершины всегда будут лежать на какой-то одной окружности, а из двух сегментов этой окружности всегда нужно выбирать наименьший





Решение

{$A+,B-,D+,E-,F-,G-,I+,L+,N+,O-,P-,Q+,R+,S+,T-,V+,X+,Y+}

{$M 65520,0,655360}
 
{$DEFINE DEBUG$}
program area;
const
  nMax = 20;
  eps = 0.0000001;
type
  tPoint = record
             x, y : extended;
           end;
  tCircle = record
              c : tPoint;
              r : extended;
            end;
  tLine = record
            A, B, C : extended;
          end;
  tBelongs = set of 1..nMax;
var
  c : array[1..nMax] of tCircle;
  points : array[0..6*nMax*nMax+1] of tPoint;
  belongs : array[0..6*nMax*nMax+1] of tBelongs;
  N, cntPoints, cntGood : integer;
  square : extended;
 
function Equal(const a, b : extended) : boolean;
begin
  Equal := abs(a-b) < eps
end;
 
function LEqual(const a, b : extended) : boolean;
begin
  LEqual := (a-b) <= Eps;
end;
 
procedure init;
var
  i, j : integer;
  fl : boolean;
begin
  assign(input, 'area.in');
  reSet(input);
{$IFNDEF DEBUG}
  assign(output, 'area.out');
  reWrite(output);
{$ENDIF}
 
  read(N);
  i := 1;
  while i <= N do begin
    read(c[i].c.x, c[i].c.y, c[i].r);
    fl := false;
    for j := 1 to i-1 do
      if Equal(c[i].c.x, c[j].c.x) and Equal(c[i].c.y, c[j].c.y) and
         Equal(c[i].r, c[j].r) then
        fl := true;
 
    if fl then
      dec(N)
    else
      inc(i);
  end;
end;
 
procedure addPoint(const p : tPoint; const s : tBelongs);
begin
  inc(cntPoints);
  points[cntPoints] := p;
 
  belongs[cntPoints] := belongs[cntPoints] + s;
end;
 
function distance(const p1, p2 : tPoint) : extended;
begin
  distance := sqrt(sqr(p1.x-p2.x) + sqr(p1.y-p2.y));
end;
 
procedure putSegment(c : tPoint; line : tLine; len, len1 : extended;
                     var p1, p2 : tPoint);
var
  line1 : tLine;
  p : tPoint;
  norm : extended;
begin
  p.x := c.x-line.B*len;
  p.y := c.y+line.A*len;
 
  with line1 do begin
    A := -line.B;
    B := line.A;
    C := -A*p.x-B*p.y;
    norm := sqrt(sqr(A)+sqr(B));
    A := A/norm; B := B/norm; C := C/norm;
  end;
 
  p1.x := p.x-line1.B*len1;
  p1.y := p.y+line1.A*len1;
  p2.x := p.x+line1.B*len1;
  p2.y := p.y-line1.A*len1;
end;
 
procedure buildLine(p1, p2 : tPoint; var line: tLine);
var
  norm : extended;
begin
  if Equal(p1.x, p2.x) and Equal(p1.y, p2.y) then begin
    p1.x := p1.x + random+1;
    p1.y := p1.y + random+1;
  end;
 
  with line do begin
    A := p2.y-p1.y;
    B := p1.x-p2.x;
    C := p2.x*p1.y-p1.x*p2.y;
 
    norm := sqrt(sqr(A)+sqr(B));
    A := A / norm;
    B := B / norm;
    C := C / norm;
  end;
end;
 
function intersectCircles(c1, c2 : tCircle; var p1, p2, p3, p4 : tPoint) : boolean;
var
  temp : tCircle;
  cos, dist, proj, proj1 : extended;
  line : tLine;
begin
  if c1.r < c2.r then begin
    temp := c1; c1 := c2; c2 := temp;
  end;
 
  dist := distance(c1.c, c2.c);
  if (dist > c1.r+c2.r) or (dist+c2.r < c1.r) then
    intersectCircles := false
  else begin
    intersectCircles := true;
    cos := (sqr(dist)+sqr(c1.r)-sqr(c2.r))/(2*dist*c1.r);
    proj := c1.r*cos;
    proj1 := c1.r*sqrt(1-sqr(cos));
    buildLine(c1.c, c2.c, line);
    putSegment(c1.c, line, proj, proj1, p1, p2);
    putSegment(c1.c, line, -proj, proj1, p3, p4);
  end;
end;
 
procedure intersectLineCircle(const line : tLine; const c : tCircle;
                              var p1, p2 : tPoint);
begin
{$IFDEF DEBUG}
  if not Equal(line.A*c.c.x+line.B*c.c.y+line.C, 0) then
    runError(111);
{$ENDIF}
 
  p1.x := c.c.x-c.r*line.B;
  p1.y := c.c.y+c.r*line.A;
  p2.x := c.c.x+c.r*line.B;
  p2.y := c.c.y-c.r*line.A;
end;
 
function onCircle(p : tPoint; k : integer) : boolean;
begin
  onCircle := Equal(sqr(p.x-c[k].c.x)+sqr(p.y-c[k].c.y), sqr(c[k].r))
end;
 
procedure intersect(var i, j : integer);
var
  p1, p2, p3, p4 : tPoint;
  line : tLine;
begin
  if (i <> j) and intersectCircles(c[i], c[j], p1, p2, p3, p4) then begin
    if onCircle(p1, i) and onCircle(p1, j) then
      addPoint(p1, [i, j]);
    if onCircle(p2, i) and onCircle(p2, j) then
      addPoint(p2, [i, j]);
    if onCircle(p3, i) and onCircle(p3, j) then
      addPoint(p3, [i, j]);
    if onCircle(p4, i) and onCircle(p4, j) then
      addPoint(p4, [i, j]);
  end;
 
  buildLine(c[i].c, c[j].c, line);
  intersectLineCircle(line, c[i], p1, p2);
  addPoint(p1, [i]);
  addPoint(p2, [i]);
  intersectLineCircle(line, c[j], p1, p2);
  addPoint(p1, [j]);
  addPoint(p2, [j]);
end;
 
function goodPoint(const p : tPoint) : boolean;
var
  i : integer;
begin
  for i := 1 to N do
    if not LEqual(sqr(p.x-c[i].c.x) + sqr(p.y-c[i].c.y), sqr(c[i].r)) then begin
      goodPoint := false;
      exit;
    end;
  goodPoint := true;
end;
 
function Angle(const x, y : extended) : extended; assembler;
asm
  fld y
  fld x
  fpatan
  fwait
end;
 
procedure Sort;
var
  med, temp : tPoint;
  tempB : tBelongs;
  i, j : integer;
begin
  med.x := 0; med.y := 0;
  for i := 1 to cntGood do begin
    med.x := med.x+points[i].x;
    med.y := med.y+points[i].y;
  end;
  med.x := med.x/cntGood;
  med.y := med.y/cntGood;
 
  for i := 1 to cntGood-1 do
    for j := i+1 to cntGood do
      if Angle(points[i].x-med.x, points[i].y-med.y) > Angle(points[j].x-med.x, points[j].y-med.y) then begin
        temp := points[i];
        points[i] := points[j];
        points[j] := temp;
        tempB := belongs[i];
        belongs[i] := belongs[j];
        belongs[j] := tempB;
      end;
end;
 
function squareArc(const c : tCircle; const p1, p2 : tPoint) : extended;
var
  alpha : extended;
begin
  alpha := abs(Angle(p1.x-c.c.x, p1.y-c.c.y)-Angle(p2.x-c.c.x, p2.y-c.c.y));
  if alpha > pi then
    alpha := 2*pi-alpha;
 
  squareArc := c.r*c.r*alpha/2;
end;
 
procedure Delete;
var
  i, j : integer;
begin
  i := 1;
  while i <= cntGood-1 do begin
    if Equal(points[i].x, points[i+1].x) and Equal(points[i].y, points[i+1].y) then begin
      belongs[i] := belongs[i] + belongs[i+1];
      for j := i+1 to cntGood-1 do begin
        points[j] := points[j+1];
        belongs[j] := belongs[j+1];
      end;
      dec(cntGood)
    end
    else
      inc(i);
  end;
 
  if Equal(points[1].x, points[cntGood].x) and Equal(points[1].y, points[cntGood].y) then
    dec(cntGood);
end;
 
function squareTriangle(const p1, p2, p3 : tPoint) : extended;
begin
  squareTriangle := abs(p1.x*(p2.y-p3.y)+p2.x*(p3.y-p1.y)+p3.x*(p1.y-p2.y))/2;
end;
 
procedure findSquare;
var
  i, j, k : integer;
  sec : tBelongs;
begin
  points[cntGood+1] := points[1];
  belongs[cntGood+1] := belongs[1];
  square := 0;
  for i := 1 to cntGood-1 do
    square := square+squareTriangle(points[1], points[i], points[i+1]);
 
  for i := 1 to cntGood do begin
    sec := belongs[i]*belongs[i+1];
    if sec = [] then
      runError(112);
    k := 0;
    for j := 1 to N do
      if (j in sec) then begin
{$IFDEF DEBUG}
        if k <> 0 then
          runError(113);
{$ENDIF}
        k := j;
{$IFNDEF DEBUG}
        Break;
{$ENDIF}
      end;
 
    square := square + squareArc(c[k], points[i], points[i+1]) -
              squareTriangle(c[k].c, points[i], points[i+1]);
  end;
end;
 
procedure solve;
var
  i, j : integer;
begin
  randomize;
  fillChar(points, sizeOf(points), 0);
  fillChar(belongs, sizeOf(belongs), 0);
 
  cntPoints := 0;
  for i := 1 to N do
    for j := 1 to N do
      intersect(i, j);
 
  cntGood := 0;
  for i := 1 to cntPoints do
    if goodPoint(points[i]) then begin
      inc(cntGood);
      points[cntGood] := points[i];
      belongs[cntGood] := belongs[i];
    end;
 
  if cntGood = 0 then
    writeLn(0)
  else begin
    Sort;
    Delete;
    findSquare;
 
    writeLn(square:0:8);
  end;
end;
 
Begin
  init;
  solve;
End.

 


© Особенности национальных задач по информатике