USACO 3.1.4 Shaping Regions

题就不贴了,想看的就去USACO找吧= =

大致就是说给你一堆矩形,每个矩形颜色都不一样,叠啊叠啊叠(= =||),然后求最终各颜色的面积...

原来做题的时候好像见过这个,当时用了最朴素的染色法- -,可惜没过几个点,一是数据范围过大,二是超时...

前两天又遇到这题了,想得脑袋都大了... 没有办法,只得求助于NOCOW了...

在其中相中了矩形切割的解法(线段树没学过),于是拜读了薛矛大牛的论文,受益匪浅(哪天把前面的线段树也看了)

言归正传,现在说一下这道题的解法...

在这里我先简化一下题,假设他给的不是矩阵,而是一堆线段覆盖来覆盖去,让你求最后各颜色的长度,当然数据范围也是超大的,让你不能用朴素的染色...

    我们来分析线段之间的关系,其实也就是重叠和不重叠的问题,对两个重叠的区间取交集大家都会吧...

假设有[X1,X2]和[X3,X4]这两个区间,交集就是[max(x1,x3),min(x2,x4)](如果左边界比右边界大就是空集),恩,现在把这两个区间变成有颜色的线段

那么交集依旧还是[max(x1,x3),min(x2,x4)],[X3,X4]这货是来覆盖[X1,X2]的,所以这个交集会被染成[X3,X4]的颜色,如果[X3,X4]不能完全覆盖住[X1,X2],那么[X1,X2]剩下的部分势必会露出来,这个剩下的部分就是[X1,X3],[X4,X2](括号内容同上),我们只需判断一下两直线交集是否为空,然后再决定切不切就行了,如果需要切割,别忘了删掉交集,因为他已经被覆盖了...

如果不理解,没关系,画个图应该就能明白个大概了,我引用下薛矛大牛论文的图片

用代码表示一下就是这样的- -

procedure cut(x1,x2,x3,x4);
var k1,k2:longint;
begin
	k1:=max(x1,x3);
	k2:=min(x2,x4);
	if x1 < k1 then add(x1,k1);
	if k2 < x2 then add(k2,x2);
end;

     谈下一话题!

     刚才说的是线段,那么现在来谈矩形,他俩有什么区别吗(当然,名字就不一样),其实也就是个一维和二维的区别

二维的矩形化成一维的切割就能达到同样的目的了!要把矩形割成块,总共分几步?两步,第一步,割X轴;第二部,割Y轴(= =||)当然,如果两个矩形不相交,我们就不去费那事了...再引用大牛论文的图片吧= =

具体做法是先在X轴方向割矩形,割下来的线段取线段左下角的坐标Y1和右上角的Y2入队

然后剩下交集线段组成的矩形,再按上面的方法在Y轴上切割,最后剩下的就是被覆盖的矩形了...不要迷恋他,他只是个

没有利用价值的矩形,如果你不想WA的话就删掉它吧....

下面简单的进行一下代码实现

      我假设(X3,Y3)(X4,Y4)为要插入的矩形,C为被切割矩形(x1,y1)(x2,y2)的颜色,ADD表示将被切过的矩形加入队列的过程,那么切割的过程就是这样的:

procedure cut(x1,y1,x2,y2,dir,c:longint);
var k1,k2:longint;
begin
	case dir of
	1:begin //X轴切割
		k1:=max(x1,x3);
		k2:=min(x2,x4);
		if x1 < k1 then add(x1,y1,k1,y2,c);
		if k2 < x2 then add(k2,y1,x2,y2,c);
		cut(k1,y1,k2,y2,2,c);
	  end;
	2:begin //Y轴切割
		k1:=max(y1,y3);
		k2:=min(y2,y4);
		if y1 < k1 then add(x1,y1,x2,k1,c);
		if k2 < y2 then add(x1,k2,x2,y2,c);
	  end;
	end;
end;

(其实就是论文上的那个)

总结一下矩形切割的步骤:

     1.读取x3,y3,x4,y4,color

     2.对于每个在新矩形下的矩形,如果它和新矩形覆盖,切割之,再把被切过的旧矩形删掉...要注意细节哦

     3.将新矩形入队

     以上就是矩形切割的思想,运用此思想,该题可以得到圆满解决。下面贴出代码,写得不好请指点。

program rect1;
type rec=record
         x1,x2,y1,y2:longint;
         color:longint;
     end;
var n,tot,x3,x4,y3,y4,x,y:longint;
    r:array[0..100000] of rec;
    cnt:array[0..10000] of longint;
procedure add(x1,y1,x2,y2,c:longint); //添加矩形过程
begin
     inc(tot);
     r[tot].x1:=x1;
     r[tot].x2:=x2;
     r[tot].y1:=y1;
     r[tot].y2:=y2;
     r[tot].color:=c;
end;
 
procedure del(num:longint);    //删除过程
begin    
     r[num]:=r[0];
end;
 
function cross(a:rec):boolean;   //判断是否交叉
begin
     if (x3 >= a.x2) or (a.x1 >= x4) then exit(false);
     if (a.y1 >= y4) or (y3 >= a.y2) then exit(false);
     exit(true);
end;
 
function max(a,b:longint):longint;
begin
     if a > b then exit(a) else exit(b);
end;
 
function min(a,b:longint):longint;
begin
     if a < b then exit(a) else exit(b);
end;
 
procedure cut(x1,y1,x2,y2,dir,c:longint);  //不解释
var k1,k2:longint;
begin
     case dir of 
     1:begin
         k1:=max(x1,x3);
         k2:=min(x2,x4);
         if x1 < k1 then add(x1,y1,k1,y2,c);
         if k2 < x2 then add(k2,y1,x2,y2,c);
         cut(k1,y1,k2,y2,2,c);
      end;
     2:begin
         k1:=max(y1,y3);
         k2:=min(y2,y4);
         if y1 < k1 then add(x1,y1,x2,k1,c);
         if k2 < y2 then add(x1,k2,x2,y2,c);
      end;
     end;
end;
 
procedure pre;
var i,j,c,p:longint;
begin
     tot:=-1;
     add(0,0,0,0,0);
     readln(x,y,n);
     add(0,0,x,y,1);
     for i:=1 to n do
     begin
         readln(x3,y3,x4,y4,c);
         for j:=1 to tot do
         if cross(r[j]) then
         begin
             cut(r[j].x1,r[j].y1,r[j].x2,r[j].y2,1,r[j].color);
             del(j);
         end;
         for j:=1 to tot do                //把删除过程制造的队列里的空矩形替换掉
         if (r[j].color=r[0].color) and (r[tot].color<>r[0].color) then
         begin
             r[j]:=r[tot];
             dec(tot);
         end;
         add(x3,y3,x4,y4,c);
     end;    
     fillchar(cnt,sizeof(cnt),0);
end;

procedure sol;
var i:longint;
begin
     for i:=1 to tot do
     cnt[r[i].color]:=cnt[r[i].color]+(r[i].x2-r[i].x1)*(r[i].y2-r[i].y1);     
end;

procedure outprint;
var i:longint;
begin
     for i:=1 to 10000 do
     if cnt[i] <> 0 then writeln(i,' ',cnt[i]);
end;
 
procedure main;
begin
     pre;
     sol;
     outprint;
end;
 
begin
     main;
end.

其实刚开始我的删除过程是直接将最后的矩形放到被删除的矩形的位置的,可是这样有可能会错过有些需要覆盖的矩形,导致WA...我的前几次就是因为这个WA的,躺在床上想好久才想明白= =

转载于:https://www.cnblogs.com/ACoder/archive/2011/04/04/2005378.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值