http://acm.pku.edu.cn/JudgeOnline/problem?id=1177
Задача: Картинки
На стене наклеено некоторое количество плакатов, фотографий и других картинок прямоугольной формы. Все их стороны либо вертикальны, либо горизонтальны. Каждый прямоугольник может быть частично или полностью перекрыт другими прямоугольниками. Периметром называется общая длина внутренних и внешних фигур, полученных путем наложения прямоугольников.
Напишите программу, вычисляющую значение полученного периметра. На рисунке ниже показан набор из 7 прямоугольников.
Соответствующие границы полученной фигуры показаны на рисунке ниже.
Вершины всех прямоугольников имеют целочисленные координаты.
Входные данные
В первой строке содержится число прямоугольников, наклеенных на стене. В каждой последующей строке приводятся целочисленные координаты нижней левой вершины и правой верхней вершины каждого прямоугольника. Значения этих координат даны как упорядоченные пары, состоящие из x-координаты и следующей за ней y-координаты.
Выходные данные
В единственной строке вывести неотрицательное целое число, которое является периметром для заданных прямоугольников.
Пример входных данных
7
-15 0 5 10
-5 8 20 25
15 -4 24 14
0 -6 16 4
2 15 10 22
30 10 36 20
34 0 40 16
Пример выходных данных
228
Обсуждение идеи решения
Решение на Pascal:
type pRect = ^rectangle; rectangle = record xmin, ymin, xmax, ymax: integer; next: pRect end; pEdge = ^edge; edge = record {vertical edges} kind: (left, right); finish: boolean; x, ymin, ymax: integer; next: pEdge end; var LRy, R: pRect; { LRy = list of rectangles } LEx: pEdge; i, N, yminG, ymaxG, intRect, boundRect: integer; Perimeter: longint; procedure update (ymin, ymax: integer; var yminG, ymaxG: integer); begin if ymin < yminG then yminG := ymin; if ymax > ymaxG then ymaxG := ymax end; procedure insertRect (var Ly, R: pRect); var pointer, previous: pRect; less: boolean; begin if Ly = nil then begin R^.next := nil; Ly := R end else begin less := true; pointer := Ly; previous := Ly; while (pointer <> nil) and less do begin if pointer^.ymin >= R^.ymin then less := false else begin previous := pointer; pointer := pointer^.next end end; if pointer = Ly then Ly := R else previous^.next := R; R^.next := pointer end end; procedure selectEdges (y: integer; var LRy: pRect; var LEx: pEdge); var LRAux: pRect; E: pEdge; greater: boolean; procedure insertEdge (E: pEdge; var LEx: pEdge); var LEAux, previous: pEdge; less: boolean; begin {ascending order in x} if LEx = nil then begin E^.next := nil; LEx := E end else begin less := true; LEAux := LEx; previous := LEx; while (LEAux <> nil) and less do if LEAux^.x >= E^.x then less := false else begin previous := LEAux; LEAux := LEAux^.next end; if LEAux = LEx then LEx := E else previous^.next := E; E^.next := LEAux end end; begin {selectEdges} greater := false; while (LRy <> nil) and (not greater) do if LRy^.ymin = y then begin LRAux := LRy; new(E); E^.kind := left; E^.finish := false; E^.x := LRy^.xmin; E^.ymin := LRy^.ymin; E^.ymax := LRy^.ymax; insertEdge(E, LEx); new(E); E^.kind := right; E^.finish := false; E^.x := LRy^.xmax; E^.ymin := LRy^.ymin; E^.ymax := LRy^.ymax; insertEdge(E, LEx); LRy := LRy^.next; dispose(LRAux) end else greater := true end; procedure deleteEdges (y: integer; var LEx: pEdge); var aux, prev: pEdge; found: boolean; begin {delete edges with ymax=y-1; mark edges with ymax=y} aux := LEx; {LEx<>nil } repeat found := aux^.ymax = y - 1; if found then begin aux := aux^.next; dispose(LEx); LEx := aux end until (aux = nil) or (not found); prev := aux; while aux <> nil do if aux^.ymax = y - 1 then if prev <> aux then begin prev^.next := aux^.next; dispose(aux); aux := prev^.next end else begin LEx := aux^.next; prev := LEx; dispose(aux); aux := LEx end else begin aux^.finish := aux^.ymax = y; prev := aux; aux := aux^.next end end; procedure scanEdges (y: integer; Lx: pEdge); var intRect, oldIntRect, totalRect, oldTotalRect, lowBRect, oldLowBRect, subtR, oldSubtR: integer; prev: pEdge; first: boolean; begin intRect := 0; oldIntRect := 0; totalRect := 0; oldTotalRect := 0; lowBRect := 0; oldLowBRect := 0; subtR := 0; oldSubtR := 0; prev := Lx; first := true; repeat if Lx^.finish then if Lx^.kind = left then subtR := subtR + 1 else subtR := subtR - 1 else begin if Lx^.kind = left then begin totalRect := totalRect + 1; if Lx^.ymax > y + 1 then intRect := intRect + 1; if Lx^.ymin = y then lowBRect := lowBRect + 1 end else {right edge} begin totalRect := totalRect - 1; if Lx^.ymax > y + 1 then intRect := intRect - 1; if Lx^.ymin = y then lowBRect := lowBRect - 1 end; if (totalRect = 0) and (oldTotalRect = 1) then Perimeter := Perimeter + 1 else if (totalRect = 1) and (oldTotalRect = 0) then if not first and (prev^.x = Lx^.x) and not prev^.finish then Perimeter := Perimeter - 1 else Perimeter := Perimeter + 1; if (oldIntRect = 0) and (oldTotalRect > 0) then Perimeter := Perimeter + Lx^.x - prev^.x; if (oldLowBRect > 0) and (oldTotalRect = oldLowBRect) then begin Perimeter := Perimeter + Lx^.x - prev^.x; if oldSubtR > 0 then Perimeter := Perimeter - 2 * (Lx^.x - prev^.x) end end; oldIntRect := intRect; oldTotalRect := totalRect; oldLowBRect := lowBRect; oldSubtR := subtR; first := false; prev := Lx; Lx := Lx^.next until Lx = nil end; begin { MAIN } Perimeter := 0; readln(N); new(R); with R^ do begin readln(xmin, ymin, xmax, ymax); yminG := ymin; ymaxG := ymax end; LRy := nil; insertRect(LRy, R); for i := 2 to N do begin new(R); with R^ do begin readln(xmin, ymin, xmax, ymax); update(ymin, ymax, yminG, ymaxG) end; insertRect(LRy, R) end; LEx := nil; for i := yminG to ymaxG do begin selectEdges(i, LRy, LEx); if LEx <> nil then begin deleteEdges(i, LEx); if LEx <> nil then scanEdges(i, LEx); end end; writeln(Perimeter); end.
Решение на С++:
#include<iostream> #include<vector> #include<algorithm> using namespace std; #define MAXN 1000000 const __int64 mininf=1000000; const __int64 maxinf=-1000000; struct Line { __int64 count,len; __int64 l,r; int lbc,rbc; __int64 nseg; }; struct node { __int64 x,down,up; bool flag; node(__int64 tx,__int64 td,__int64 tu,bool tf):x(tx),down(td),up(tu),flag(tf) { } }; __int64 miny,maxy; vector< node > vec; Line Ltree[MAXN]; bool operator< (const node& a,const node& b) { return a.x<b.x; } void build(__int64 l,__int64 r,int step) { Ltree[step].count=0;Ltree[step].len=0; Ltree[step].lbc=false;Ltree[step].rbc=false; Ltree[step].nseg=0; Ltree[step].r=r;Ltree[step].l=l; if(r-l>1) { build(l,(l+r)/2,2*step); build((l+r)/2,r,2*step+1); } } void update(int step) { if(Ltree[step].count>0) { Ltree[step].len=Ltree[step].r-Ltree[step].l; Ltree[step].nseg=1; } else if(Ltree[step].r-Ltree[step].l>1) { Ltree[step].len=Ltree[2*step].len+Ltree[2*step+1].len; Ltree[step].nseg=Ltree[2*step].nseg+Ltree[2*step+1].nseg; if(Ltree[step].nseg&&Ltree[2*step].rbc&&Ltree[2*step+1].lbc) Ltree[step].nseg--; } else { Ltree[step].len=0; Ltree[step].nseg=0; } } void insert(__int64 l,__int64 r,int step) { if(Ltree[step].l==l) Ltree[step].lbc++; if(Ltree[step].r==r) Ltree[step].rbc++; if(l==Ltree[step].l&&Ltree[step].r==r) Ltree[step].count++; else { __int64 mid=(Ltree[step].l+Ltree[step].r)/2; if(r<=mid) insert(l,r,2*step); else if(l>=mid) insert(l,r,2*step+1); else { insert(l,mid,2*step); insert(mid,r,2*step+1); } } update(step); } void del(__int64 l,__int64 r,int step) { if(Ltree[step].l==l) Ltree[step].lbc--; if(Ltree[step].r==r) Ltree[step].rbc--; if(l==Ltree[step].l&&Ltree[step].r==r) Ltree[step].count--; else { __int64 mid=(Ltree[step].l+Ltree[step].r)/2; if(r<=mid) del(l,r,2*step); else if(l>=mid) del(l,r,2*step+1); else { del(l,mid,2*step); del(mid,r,2*step+1); } } update(step); } int main() { int n; __int64 x1,x2,y1,y2; while(scanf("%d",&n)==1) { miny=mininf; maxy=maxinf; for(int i=0;i<n;i++) { scanf("%I64d%I64d%I64d%I64d",&x1,&y1,&x2,&y2); vec.push_back(node(x1,y1,y2,true)); vec.push_back(node(x2,y1,y2,false)); miny=min(miny,y1); maxy=max(maxy,y2); } sort(vec.begin(),vec.end()); build(miny,maxy,1); __int64 peri=0; int m=vec.size(); __int64 lastlen=0,lastseg=0; for(int i=0;i<m;i++) { if(vec[i].flag) insert(vec[i].down,vec[i].up,1); else del(vec[i].down,vec[i].up,1); peri+=abs(Ltree[1].len-lastlen); lastlen=Ltree[1].len; if(i) peri+=2*(vec[i].x-vec[i-1].x)*lastseg; lastseg=Ltree[1].nseg; } printf("%I64d\n",peri); } return 0; }