Описание В картинной галерее, имеющей форму N-угольника, расположено M люстр, которые мы будем считать точечными источниками света. Точка стены галереи называется освещенной, если из нее видна хотя бы одна из люстр. Неосвещенным участком будем называть максимальное связное множество точек стены галереи, ни одна из которых не освещена (участок может содержать углы галереи) Задание Напишите программу, определяющую все неосвещенные участки Входные данные Первая строка входного файла содержит два целых числа N и M (1≤N,M≤30). В каждой из следующих N строк записаны координаты очередного угла галереи. Углы перечислены в порядке обхода стены по часовой стрелке. Далее идут M строк, каждая из которых содержит координаты очередной из люстр. Все координаты являются вещественными числами и разделяются пробелом Выходные данные В первую строку выходного файла выведите количество неосвещенных участков S. Каждая из следующих S строк должна содержать описание очередного из участков в виде тройки чисел, разделенных пробелом. Первые два числа определяют координаты начальной точки участка, третье - его длину Участок должен продолжаться на указанную длину в направлении обхода стены по часовой стрелке. Никакие два участка не должны иметь общих точек. Числа, определяющие участок, должны быть выведены не менее чем с 3 верными значащими цифрами Например: PGALLERY.IN 5 1 0 0 0 5 4 5 2 3 5 0 3.0 1.0 PGALLERY.OUT 1 1 5 5.82843 Идеи Пересечение отрезков Комментарии Рассмотрим решение задачи, если источник света один. Разобьем все стороны N-угольника на отрезки так, чтобы каждый отрезок был либо целиком освещен, либо целиком неосвещен. Для этого проведем из источника света лучи через те вершины N-угольника, угол при которых больше 180°, и найдем ближайшие точки пересечения этих лучей со сторонами N-угольника (касания не рассматриваются). Эти точки и дадут нам требуемое разбиение. Теперь для каждого отрезка надо проверить, освещен он или нет. Проведем луч из источника света через середину проверяемого отрезка. Найдем точки пересечения этого луча со сторонами N-угольника и среди них отыщем наименее удаленную от источника света. Если это середина нашего отрезка, то отрезок освещен, если нет, то та сторона, которой принадлежит эта точка, закрывает проверяемый отрезок от источника света Проведем эту процедуру для остальных источников света, разбивая имеющиеся неосвещенные отрезки на более мелкие (для освещенных отрезков это бессмысленно). При выводе соседние неосвещенные участки надо объединить Решение {$A+,B-,D+,E-,F-,G-,I+,L+,N+,O-,P-,Q+,R+,S+,T+,V+,X+} {$M 65520,0,655360} program pgallery; const Delta=0.000001; Max=30; type real = extended; Point=record x,y:real end; Line=record A,B,C:real end; var N,M,Cnt,Tests:integer; A:Array[1..Max+1] of Point; (* Галерея *) B:Array[1..Max] of Point; (* Лампы *) Lin:Array[1..Max] of Line; (* Стены галереи *) All:Array[1..Max*Max] of Point; InL:Array[1..Max] of boolean; Procedure Lines(p1,p2:Point;var L:Line); Begin L.A:=p2.y-p1.y; L.B:=p1.x-p2.x; L.C:=p1.y*p2.x-p1.x*p2.y End(*Lines*); Function Eq(A,B:real):Boolean; Begin If abs(A-B) Else Eq:=false End(*Eq*); Function More(A,B:real):boolean; Begin If A-B>-Delta then More:=true Else More:=false End(*More*); Function In_(B,A,C:Point):boolean; Begin If ( More(C.x,B.x) and More(B.x,A.x) or More(B.x,C.x) and More(A.x,B.x) ) and ( More(C.y,B.y) and More(B.y,A.y) or More(B.y,C.y) and More(A.y,B.y) ) then In_:=true Else In_:=false End(*In_*); Procedure Init; Var i,j:integer; Begin read(N, M); FillChar(A,SizeOf(a),0); For i:=1 to N do read(A[i].x,A[i].y); A[N+1]:=A[1]; FillChar(B,SizeOf(B),0); For i:=1 to M do read(B[i].x,B[i].y); FillChar(Lin,SizeOf(Lin),0); For i:=1 to N do (* Записываем ур-ня сторон *) Lines(A[i],A[i+1],Lin[i]); FillChar(All,SizeOf(All),0); Move(A,All,SizeOf(A)); Cnt:=N; For i:=1 to M do (* InL[i]=true => лампа i лежит на стороне *) Begin InL[i]:=false; For j:=1 to N do If Eq(B[i].x*Lin[j].A+B[i].y*Lin[j].B+Lin[j].C,0) and In_(B[i],A[j],A[j+1]) then Begin InL[i]:=true; Break End; End End(*Init*); Procedure Cut(L1,L2:Line;var P:Point); (* Пересечение 2 прямых *) Var BigM,Dx,Dy:real; Begin BigM:=L1.A*L2.B-L2.A*L1.B; If Eq(BigM,0) then Begin P.x:=1E20; P.y:=1E20 End else Begin Dx:=L1.B*L2.C-L1.C*L2.B; Dy:=L1.C*L2.A-L1.A*L2.C; P.x:=Dx/BigM; P.y:=Dy/BigM; End; End(*Cut*); Function EqPoint(A,B:Point):boolean; (* Рав-во точек *) Begin If Eq(A.x,B.x) and Eq(A.y,B.y) then EqPoint:=true Else EqPoint:=false End(*EqPoint*); Procedure NewPoint(P,Left:Point); (* Добавление новой точки в многоугольник *) Var i,j:integer; Begin For i:=1 to Cnt do If EqPoint(All[i],P) then Exit; i:=1; While not EqPoint(All[i],Left) do inc(i); inc(i); While In_(All[i],P,Left) do inc(i); For j:=Cnt downto i do All[j+1]:=All[j]; All[i]:=P; inc(Cnt) End(*NewPoint*); Procedure NewPoints; (* Новые точки *) Var i,j,k:integer; L:Line; P:Point; Begin For i:=1 to M do For j:=1 to N do Begin Lines(B[i],A[j],L); For k:=1 to N do Begin Cut(L,Lin[k],P); If In_(A[j],B[i],P) and In_(P,A[k],A[k+1]) then NewPoint(P,A[k]) End; End; End(*NewPoints*); Function InGal(P:Point):boolean; (* Принадлежность точки галерее *) Var L:Line; i,j,cnt:integer; P1:Point; Begin L.A:=1; L.B:=0; L.C:=-P.x; cnt:=0; For i:=1 to N do Begin Cut(L,Lin[i],P1); If In_(P1,A[i],A[i+1]) and More(P.y,P1.y) then If not( EqPoint(P1,A[i]) or EqPoint(P1,A[i+1]) ) then inc(cnt) Else If EqPoint(P1,A[i]) and More(A[i+1].x,A[i].x) then inc(cnt) Else If EqPoint(P1,A[i+1]) and More(A[i].x,A[i+1].x) then inc(cnt) End; For i:=1 to N do If Eq(P.x*Lin[i].A+P.y*Lin[i].B+Lin[i].C,0) and In_(P,A[i],A[i+1]) then cnt:=1; If cnt mod 2=1 then InGal:=true Else InGal:=false; End(*InGal*); Function Out(A:Point;i:integer):boolean; (* Лежит ли отрезок [A,B[i]] вне галереи *) Var P:Point; Begin If not InL[i] then Begin Out:=false; Exit End; P.x:=(A.x+B[i].x)/2; P.y:=(A.y+B[i].y)/2; If InGal(P) then Out:=false Else Out:=true End(*Out*); Function Sune(P:Point):boolean; (* Освещена ли точка P *) Var i,j:integer; L:Line; P1:Point; Fl:boolean; Begin For i:=1 to M do Begin If Out (P,i) then Continue; Lines(B[i],P,L); Fl:=true; For j:=1 to N do Begin Cut(L,Lin[j],P1); If In_(P1,B[i],P) and In_(P1,A[j],A[j+1]) and not ( EqPoint(P,P1) or EqPoint(B[i],P1) ) then Begin Fl:=false; Break End End; If Fl then Begin Sune:=true; Exit End End; Sune:=false End(*Sune*); Function Dist(A,B:Point):real; Begin Dist:=sqrt(sqr(A.x-B.x)+sqr(A.y-B.y)) End(*Dist*); Procedure Look; (* Просматриваем все точки в All *) Var i,K:integer; P:Point; Flag:boolean; Otr:Array[1..Max*Max] of record P:Point; L:real end; Begin All[Cnt+1]:=All[1]; Flag:=true; K:=0; FillChar(Otr,SizeOf(Otr),0); For i:=1 to Cnt do Begin P.x:=(All[i].x+All[i+1].x)/2; P.y:=(All[i].y+All[i+1].y)/2; If not Sune(P) then If Flag then Begin inc(K); Otr[K].P:=All[i]; Otr[k].L:=Dist(All[i],All[i+1]); Flag:=false; End Else Otr[K].L:=Otr[K].L+Dist(All[i],All[i+1]) Else If not Flag then Flag:=true End; (* Вывод результата *) writeLn(K); For i:=1 to K do writeln(Otr[i].P.x:0:4,' ',Otr[i].P.y:0:4,' ',Otr[i].L:0:4) End; End(*Look*); BEGIN Assign(input,'pgallery.in'); Reset(input); Assign(output, 'pgallery.out'); reWrite(output); Begin Init; NewPoints; Look; End; close(input);close(output) END. |
© Особенности национальных задач по информатике |