扫描线

HDU 1542 Atlantis

 

如图,我们来求两个矩形所覆盖的总面积

我们先定义一条平移于y轴的扫描线(如图,黄线标识),另其从左到右逐个扫描每个所给矩形的竖边,我们命名为线1,线2,线3,线4(横坐标为下,x1,x2,x3,x4),每个矩形的纵坐标的上下限依次为y1 y2 y3 y4

当扫描线从线1扫描到线2的时候,扫描到的面积为s1=(y3-y1)*(x2-x1)

当扫描线从线2扫到线3的时候,面积s2=(y4-y1)*(x3-x2)

……

这样总面积s=s1+s2+s3

实现

我们先定义一个结构体表示每个矩形中的竖边

struct line{
	double x;//横坐标
	double y_up;//竖线的纵坐标上限
	double y_down;//下限
	int state;//1表示矩形的左竖边,-1表示右竖边
}q[maxn];

还要对存储竖边的结构体根据x进行从小到大的排序

然后呢我每次扫描到一个竖边都对询问其所扫描到的矩形高度,这个过程我们是通过将y轴上的每个区间的线段树上

我们定义一个结构体表示线段树的节点

struct node{
	double left;//线段树的左结界,也就是y轴上的大的坐标
    double right;//右结界,同理
	int cover;//目前扫描线所覆盖的矩形个数
	double len;//目前扫描线于矩形重合的长度
}p[maxn];

对于每个区间节点,cover 表示目前扫描线覆盖的矩形数量,如果cover==0,说明在这个区间并没有全部与矩形重合,只是部分子区间与矩形重合,每次扫描到矩形竖边,都对线段树进行更新,最后将线段树区间与矩形重合的长度全部返回到线段树的主根,然后用这个长度乘以扫描线从上一个竖边到这个竖边所走的距离(x轴上距离)得出面积

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define maxn 10000
using namespace std;
struct node{
	double left,right;
	int cover;
	double len;
}p[maxn];

struct line{
	double x;
	double y_up;
	double y_down;
	int state;
}q[maxn];
bool cmp(line a, line b){
	return a.x<b.x;
}


double point[maxn];
void build(int root,int left,int right){
	p[root].left=point[left];
	
	p[root].right=point[right];
	if(right-left>1){
		 int mid=(left+right)>>1;
		 build(root<<1|1,left,mid);
		 build((root<<1)+2,mid,right);
	}
} 


void update(int root,double left,double right,int val){
	
	
	if(p[root].left==left&&p[root].right==right){
		p[root].cover+=val;  //更新 
		
		if(p[root].cover==0) 
			p[root].len=p[root<<1|1].len+p[(root<<1)+2].len;
		if(p[root].cover!=0)
			p[root].len=p[root].right-p[root].left; 
		return;
	}
	
	int left_root=root<<1|1; //左儿子 
	int right_root=(root<<1)+2; //右儿子 
	int mid=p[left_root].right;
	
	
	if(right<=p[left_root].right){
		update(left_root,left,right,val);
	}
	else if(left>=p[right_root].left){
		update(right_root,left,right,val);
	}
	else{
		update(left_root,left,mid,val);
		update(right_root,mid,right,val);
	}
	if(p[root].cover==0)
	p[root].len=p[right_root].len+p[left_root].len;
}
int main()     
{
	double x,y,x1,y1;
	int cnt=0;
	int n;
	int t=0;
	while(cin>>n&&n){
		t++;
		cnt=0;
		for(int i=0;i<n;i++){
			cin>>x>>y>>x1>>y1;  //左下角坐标  右上角坐标 
			q[cnt]=(line){x,y1,y,1};
			point[cnt]=y;
			q[++cnt]=(line){x1,y1,y,-1};
			point[cnt]=y1;
			cnt++;
		}
		sort(q,q+cnt,cmp);
		sort(point,point+cnt);
		build(0,0,cnt-1);
		double s=0;
		
		update(0,q[0].y_down,q[0].y_up,q[0].state);
		for(int i=1;i<cnt;i++){                    
			s+=p[0].len*(q[i].x-q[i-1].x);
			update(0,q[i].y_down,q[i].y_up,q[i].state);
		}                 
		cout<<"Test case #"<<t<<endl;
		printf("Total explored area: %.2lf\n\n",s);
	}
	return 0;
}  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值