hdu1542 面积交详细讲解

使用线段树计算矩形面积交
这篇博客详细介绍了如何通过线段树计算矩形的面积交。首先,将矩形转换为线段,并根据x坐标排序。然后,遍历线段,遇到下边时在线段树上增加覆盖,遇到上边则减少覆盖,并计算当前的并面积。这种方法避免了面积的重复计算。

下面资料 均为网上收集

给定一个矩形的左下角坐标和右上角坐标分别为:(x1,y1)、(x2,y2),对这样的一个矩形,我们构造两条线段,一条定位在x1,它在y坐标的区间是[y1,y2],并且给定一个cover域值为1;另一条线段定位在x2,区间一样是[y1,y2],给定它一个cover值为-1。根据这样的方法对每个矩形都构造两个线段,最后将所有的线段根据所定位的x从左到右进行排序。

上图中,红色的字体表示的是该线段的cover值。刚刚开始的时候,线段树上的cover值都为0,但第一根线段(x==0)插入线段树的之后,我们将线段树上的cover加上该线段的cover,那么,此时线段树上被该线段覆盖的位置上的cover的值就为1,下次再插入第二根线段(x==1)此时发现该线段所覆盖的区间内,有一部分线段树的cover为0,另有一部分为1,仔细观察,但插入第二个线段的时候,如果线段树上cover已经为1的那些区间,和现在要插入的第二根线段之间,是不是构成了并面积?还不明白?看下图,绿色部分即为插入第二根线段后得到的并面积

够清楚了吧!也就是说,我们插入某跟线段的时候,只要看该线段所在区间上的cover是否大于等于1,如果是,那么就可以将并面积值加上(目前线段的x定位 - 上一线段的x定位)*(该区间的大小)

 

经典的矩形面积并;线段树保存当前线段的总长度

1。对x轴建树,将矩形分成下边+上边+高;

2. 按y升序排序

3.y从低到高{

      if(遇到下边)就将长度为x.r-x.l的线段插入线段树(该区间的覆盖次数加1)

      else(遇到上边)就将长度为x.r-x.l的从线段树中删除(该区间的覆盖次数减1)

     同时计算当前面积,即线段长度和l*h(当前线段与下一条线段的高度差)

}

坐标有浮点数,先离散化会方便些。
对于一个矩形(x1, y1, x2, y2),只取上边和下边,结构体ss[] 记录一个边的左右坐标(ss[].x1,ss[].x2)和高度(ss[].y1 或ss[].y2),再拿一个变量ss[].s记录这个边是上边还是下边,下边记录为1,上边记录为-1。
按高度排序,对横轴建树,从低到高扫描一遍。若下边,则加入线段树;若上边,则从线段树上去掉。
cnt[] 表示下边比上边多几个。 sum[]表示区间中可用线段,sum[1]则表示可用线段总长度。

拿图来简单说明下:

【hdu】 <wbr>1542 <wbr>Atlantis <wbr> <wbr>[线段树 <wbr>矩形面积的并]
这是两个矩形,矩形1(1, 1, 10, 10)   矩形2(5, 5, 20,20)


【hdu】 <wbr>1542 <wbr>Atlantis <wbr> <wbr>[线段树 <wbr>矩形面积的并]
离散化的图像。


 

图片

 注意本题是一遍添加 一边释放  不会造成面积的重复计算


最后,其实是从下往上,这么求出来的面积。

1542这个题用线段树做,有两个收获,一是复习了类似的离散化,段更新求面积周长的方法,另外,就是学到了如何把“点值”转换成“段值”,线段树本质上,是对段的操作,但坐标值,是一个点值,所以要想用线段树解决,就必须把点值转换成段值,首先离散化,离散成一段一段的,把大的点值-1,当成段值,可以想象,一个含有n个点的线段,其实只有n-1个小段,所以要-1,当然要考虑只有一个点的情况要特殊处理,然后在更新总和的时候,要把段还原成点,这样就可以用“段”求“点”了~
今天下午一直昏昏沉沉,提不起精神,勉强做了这个题,希望是个转折吧
#include "stdio.h"
#include "algorithm"
using namespace std;
struct {
      int l,r;
      int cnt;
      double sum;
      int getmid(){
            return (l+r)/2;
      }
}st[6666];//横线段的线段树

struct SS{
      double l,r;
      double h;
      int s;
}ss[6666];//横线段
double pos[6666];//横坐标
int k;//横坐标的个数
int bin(int c);
bool cmp(struct SS&a,struct SS&b){
      return a.h<b.h; //从小到大
}

void build(int a,int b,int ind){
      st[ind].l=a;
      st[ind].r=b;
      st[ind].cnt=0;
      st[ind].sum=0;
      if(a==b)
            return ;
      int mid=st[ind].getmid();
      build(a,mid,ind*2);
      build(mid+1,b,ind*2+1);
}
void update(int ind){
      if(st[ind].cnt){
            st[ind].sum=pos[st[ind].r+1]-pos[st[ind].l];//在计算和时,还原点值,所以+1
      }else if(st[ind].l==st[ind].r){
            st[ind].sum=0;
      }
      else{
            st[ind].sum=st[ind*2].sum+st[ind*2+1].sum;
      }
}
void insert(int a,int b,int c,int ind){
      if(a<=st[ind].l&&st[ind].r<=b){
            st[ind].cnt+=c;
      }else{
            int mid=st[ind].getmid();
            if(a<=mid)
                  insert(a,b,c,ind*2);
            if(b>mid)
                  insert(a,b,c,ind*2+1);
      }
      update(ind);
}
int bin(double c){
      int p,q;
      p=0;
      q=k-1;
      while(p<=q){
            int mid=(p+q)/2;
            if(pos[mid]==c)
                  return mid;
            if(pos[mid]<c)
                  p=mid+1;
            else
                  q=mid-1;
      }
      return -1;
}
int main(){
      int n,m;
      double x1,x2;
      int cx=-1;
      for(;;){
            scanf("%d",&n);
            if(n==0)
                  break;
            cx++;
            int i,j;
            m=0;
            for(i=0;i<n;i++){
                  double a,b,c,d;
                  scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
                  pos[m]=ss[m].l=ss[m+1].l=a;
                  ss[m].h=b;
                  ss[m].s=1;
                  pos[m+1]=ss[m].r=ss[m+1].r=c;
                  ss[m+1].h=d;
                  ss[m+1].s=-1;
                  m+=2;
            }
            sort(pos,pos+m);
            sort(ss,ss+m,cmp);
            for(i=0,k=0;i<m;i++){
                  if(i==0||pos[i]!=pos[i-1]){
                        pos[k++]=pos[i];
                  }
            }
            build(0,k-1,1);
            double ret=0;
            for(i=0;i<m-1;i++){
                 
                  x1=bin(ss[i].l);
                  x2=bin(ss[i].r)-1;//求出离散化后的点对应的序号,换成段的序号要-1
                  if(x2<x1){//两个点重复时,特殊处理!
                        ret+=st[1].sum*(ss[i+1].h-ss[i].h);
                        continue;
                  }
                  insert(x1,x2,ss[i].s,1);
                  ret+=st[1].sum*(ss[i+1].h-ss[i].h);
            }
            printf("Test case #%d\n",cx+1);
            printf("Total explored area: %.2lf\n\n",ret);
      }
      return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值