Shaping Regions解题报告
原题:
N个不同的颜色的不透明的长方形(1 <= N <= 1000)被放置在一张宽为A长为B的白纸上。
这些长方形被放置时,保证了它们的边于白纸的边缘平行。
所有的长方形都放置在白纸内,所以我们会看到不同形状的各种颜色。
坐标系统的原点(0,0)设在这张白纸的左下角,而坐标轴则平行于边缘。
PROGRAM NAME: rect1
INPUT FORMAT
每行输入的是放置长方形的方法。
第一行输入的是那个放在底的长方形(即白纸)。
第 1 行: A , B 和 N, 由空格分开 (1 <=A, B<=10,000)
第 2 到N+1行: 为五个整数 llx, lly, urx, ury, color 这是一个长方形的左下角坐标,右上角坐标和颜色。
颜色 1和底部白纸的颜色相同。
SAMPLE INPUT (file rect1.in)
20 20 3
2 2 18 18 2
0 8 19 19 3
8 0 10 19 4
OUTPUT FORMAT
输出文件应该包含一个所有能被看到颜色连同该颜色的总面积的清单( 即使颜色的区域不是连续的),按color的增序顺序。
不要显示没有区域的颜色。
SAMPLE OUTPUT (file rect1.out)
1 91
2 84
3 187
4 38
知识预备:矩形切割
考虑两个矩形a,b,其中a覆盖在b上,则b可以被看到的部分为b-a∩b。
若a没有与b相交,则必然出现了以下4种情况之一:(由于矩形的顶点没有包含在矩形内,所以若坐标相等也算无交集)
1. a.x1>=b.x2 (a在b右方)
2. a.x2<=b.x1 (a在b左方)
3. a.y1>=b.y2 (a在b上方)
4. a.y2<=b.y1 (a在b下方)
若a与b相交,同样也会出现4种情况(花括号内的可省略,因为假设a与b相交):
1. b.x1<a.x1{<b.x2}
2. b.x2>a.x2{>b.x1}
3. b.y1<a.y1{<b.y2}
4. b.y2>a.y2{>b.y1}
接下来就轮到矩形切割了:
以下图为例:
当矩形a覆盖在矩形b上时,所能看到的部分为S1+S2+S3+S4,我们的目标就是求出S1+S2+S3+S4。
对于相交的4种情况分别进行讨论: (每个矩形用左下顶点和右上顶点坐标确定)
1. 当且仅当b.x1<a.x1<b.x2时,存在矩形S1,且 S1.x1=b.x1,S1.y1=b.y1, S1.x2=a.x1,S1.y2:=b.y2;
2. 当且仅当b.x1<a.x2<b.x2时,存在矩形S3,且 S3.x1=a.x2,S3.y1=b.y1, S3.x2=b.x2,S3.y2:=b.y2;
3. 当且仅当b.y1<a.y1<b.y2时,存在矩形S2,且 S2.x1=a.x1,S2.y1=b.y1, S2.x2=a.x2,S2.y2:=a.y1;
4. 当且仅当b.y1<a.y2<b.y2时,存在矩形S4,且 S4.x1=a.x1,S4.y1=a.y2, S4.x2=a.x2,S4.y2:=b.y2;
基本算法
至此,本题的算法便不难得到了。
数据结构:
- Type
- Rectangle=x1,y1,x2,y2,color:longint;
维护一个表list存放已经加入了的矩形:
- Var
- List:array[1..MAX_N] of Rectangle
我们还需要两个过程,加入和删除矩形:
- Procedure ADD(rec:rectangle);
- begin
- inc(count);
- list[count]:=rec;
- end;
- Procedure DEL(index:longint);
- begin
- list[index]:=list[count];
- dec(count);
- end;
当我们每加入一个矩形(1 to n),都得扫描表中所有数据,若有矩形与新矩形相交,则进行矩形切割,即加入上图中的S1,S2,S3,S4,删除矩形b,最后扫描一遍list,即可统计各种颜色的面积。
漂浮法
还有一种更强的算法,来自Christoph Roick or Maigo,甚至可以用递归直接统计,完全代替list。暂且称为漂浮法。
主要思想如下:
以逆序来进行放置,即n to 1。逆序的好处在于放置一个矩形后,俯视看到的就是最终俯视该矩形应该看到的。因为挡着它的矩形在之前已经放置好了,所以可直接统计,为递归创造了条件。每放一个矩形,可以想象成将其扔入一密度很大的海水底部,海分成了n层,然后矩形开始向上浮。在上浮过程中若碰撞到其他的矩形则断裂成几个小矩形,继续上浮,直到浮出水面。于是想到用个递归来模拟上浮过程。
Maigo的代码如下(仅仅40多行!):
program rect1;
const
maxn=1000;
maxcolor=2500;
var
fin,fout:text;
x1,y1,x2,y2,color:array[0..maxn]of word;
area:array[1..maxcolor]of longint;
n,i,now:longint;
procedure cal(l,r,b,t,z:longint); {l,r,b,t分别对应当前矩形的x1,x2,y1,y2,z是当前层的编号}
begin
while (z<=n) {仍然在水中}and ((r<=x1[z]) or (l>=x2[z]) or (t<=y1[z]) or (b>=y2[z])) do inc(z);{若当前层没有发生碰撞,则继续上浮}
if z>n then begin inc(area[now],(r-l)*(t-b));exit;end;{浮出水面,统计}
if l<x1[z] then begin cal(l,x1[z],b,t,z+1);l:=x1[z];end;
if r>x2[z] then begin cal(x2[z],r,b,t,z+1);r:=x2[z];end;
if b<y1[z] then cal(l,r,b,y1[z],z+1);{可省略更新,因为后面没有引用了}
if t>y2[z] then cal(l,r,y2[z],t,z+1); {同上}
end;
begin
assign(fin,'rect1.in');
reset(fin);
read(fin,x2[0],y2[0],n);
x1[0]:=0;y1[0]:=0;color[0]:=1;
for i:=1 to n do
read(fin,x1[i],y1[i],x2[i],y2[i],color[i]);
close(fin);
for i:=n downto 0 do begin
now:=color[i];{记录当前矩形的颜色}
cal(x1[i],x2[i],y1[i],y2[i],i+1);
end;
assign(fout,'rect1.out');
rewrite(fout);
for i:=1 to maxcolor do
if area[i]>0 then writeln(fout,i,' ',area[i]);
close(fout);
end.